gre: Check whether IPv6 is compiled into the kernel.
authorJesse Gross <jesse@nicira.com>
Wed, 19 May 2010 00:30:42 +0000 (17:30 -0700)
committerJesse Gross <jesse@nicira.com>
Wed, 19 May 2010 19:33:33 +0000 (12:33 -0700)
Add guards to check whether IPv6 is supported by the kernel instead
of causing the module to fail to load.  If IPv6 is not supported
these packets can still be encapsulated but they will not receive any
special treatment such as path MTU discovery.

INSTALL.Linux
datapath/vport-gre.c

index 20a0dd8..c3cf2f8 100644 (file)
@@ -48,6 +48,10 @@ INSTALL.userspace for more information.
       and NET_ACT_POLICE, either built-in or as modules.
       (NET_CLS_POLICE is obsolete and not needed.)
 
+      If GRE tunneling is being used it is recommended that the kernel
+      be compiled with IPv6 support (CONFIG_IPV6).  This allows for
+      special handling (such as path MTU discovery) of IPv6 packets.
+
     - To build a kernel module, you need the same version of GCC that
       was used to build that kernel.
 
index dbfc100..237835b 100644 (file)
@@ -22,7 +22,9 @@
 #include <net/icmp.h>
 #include <net/inet_ecn.h>
 #include <net/ip.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #include <net/ipv6.h>
+#endif
 #include <net/protocol.h>
 #include <net/route.h>
 #include <net/xfrm.h>
@@ -377,6 +379,7 @@ ipv4_build_icmp(struct sk_buff *skb, struct sk_buff *nskb,
        icmph->checksum = csum_fold(nskb->csum);
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 static bool
 ipv6_should_icmp(struct sk_buff *skb)
 {
@@ -452,13 +455,14 @@ ipv6_build_icmp(struct sk_buff *skb, struct sk_buff *nskb, unsigned int mtu,
                                                + payload_length,
                                                ipv6h->nexthdr, nskb->csum);
 }
+#endif /* IPv6 */
 
 static bool
 send_frag_needed(struct vport *vport, const struct mutable_config *mutable,
                 struct sk_buff *skb, unsigned int mtu, __be32 flow_key)
 {
        unsigned int eth_hdr_len = ETH_HLEN;
-       unsigned int total_length, header_length, payload_length;
+       unsigned int total_length = 0, header_length = 0, payload_length;
        struct ethhdr *eh, *old_eh = eth_hdr(skb);
        struct sk_buff *nskb;
 
@@ -469,7 +473,9 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable,
 
                if (!ipv4_should_icmp(skb))
                        return true;
-       } else {
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
                if (mtu < IPV6_MIN_MTU)
                        return false;
 
@@ -481,6 +487,9 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable,
                if (!ipv6_should_icmp(skb))
                        return true;
        }
+#endif
+       else
+               return false;
 
        /* Allocate */
        if (old_eh->h_proto == htons(ETH_P_8021Q))
@@ -491,12 +500,16 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable,
                header_length = sizeof(struct iphdr) + sizeof(struct icmphdr);
                total_length = min_t(unsigned int, header_length +
                                                   payload_length, 576);
-       } else {
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else {
                header_length = sizeof(struct ipv6hdr) +
                                sizeof(struct icmp6hdr);
                total_length = min_t(unsigned int, header_length +
                                                  payload_length, IPV6_MIN_MTU);
        }
+#endif
+
        total_length = min(total_length, mutable->mtu);
        payload_length = total_length - header_length;
 
@@ -523,8 +536,10 @@ send_frag_needed(struct vport *vport, const struct mutable_config *mutable,
        /* Protocol */
        if (skb->protocol == htons(ETH_P_IP))
                ipv4_build_icmp(skb, nskb, mtu, payload_length);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        else
                ipv6_build_icmp(skb, nskb, mtu, payload_length);
+#endif
 
        /* Assume that flow based keys are symmetric with respect to input
         * and output and use the key that we were going to put on the
@@ -672,8 +687,10 @@ ecn_encapsulate(u8 tos, struct sk_buff *skb)
 
        if (skb->protocol == htons(ETH_P_IP))
                inner = ((struct iphdr *)skb_network_header(skb))->tos;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        else if (skb->protocol == htons(ETH_P_IPV6))
                inner = ipv6_get_dsfield((struct ipv6hdr *)skb_network_header(skb));
+#endif
        else
                inner = 0;
 
@@ -701,7 +718,9 @@ ecn_decapsulate(u8 tos, struct sk_buff *skb)
                                return;
 
                        IP_ECN_set_ce((struct iphdr *)(nw_header + skb->data));
-               } else if (protocol == htons(ETH_P_IPV6)) {
+               }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+               else if (protocol == htons(ETH_P_IPV6)) {
                        if (unlikely(!pskb_may_pull(skb, nw_header
                            + sizeof(struct ipv6hdr))))
                                return;
@@ -709,6 +728,7 @@ ecn_decapsulate(u8 tos, struct sk_buff *skb)
                        IP6_ECN_set_ce((struct ipv6hdr *)(nw_header
                                                          + skb->data));
                }
+#endif
        }
 }
 
@@ -809,8 +829,10 @@ gre_err(struct sk_buff *skb, u32 info)
 
        if (skb->protocol == htons(ETH_P_IP))
                tot_hdr_len += sizeof(struct iphdr);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        else if (skb->protocol == htons(ETH_P_IPV6))
                tot_hdr_len += sizeof(struct ipv6hdr);
+#endif
        else
                goto out;
 
@@ -825,7 +847,9 @@ gre_err(struct sk_buff *skb, u32 info)
                                goto out;
                }
 
-       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
                if (mtu < IPV6_MIN_MTU) {
                        unsigned int packet_length = sizeof(struct ipv6hdr) +
                                              ntohs(ipv6_hdr(skb)->payload_len);
@@ -837,6 +861,7 @@ gre_err(struct sk_buff *skb, u32 info)
                                goto out;
                }
        }
+#endif
 
        __pskb_pull(skb, tunnel_hdr_len);
        send_frag_needed(vport, mutable, skb, mtu, key);
@@ -942,7 +967,9 @@ build_packet(struct vport *vport, const struct mutable_config *mutable,
                                goto error_free;
                }
 
-       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
                unsigned int packet_length = skb->len - ETH_HLEN
                        - (eth_hdr(skb)->h_proto == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
 
@@ -955,6 +982,7 @@ build_packet(struct vport *vport, const struct mutable_config *mutable,
                                goto error_free;
                }
        }
+#endif
 
        skb_reset_transport_header(skb);
        new_iph = (struct iphdr *)skb_push(skb, mutable->tunnel_hlen);
@@ -996,7 +1024,6 @@ gre_send(struct vport *vport, struct sk_buff *skb)
        const struct mutable_config *mutable = rcu_dereference(gre_vport->mutable);
 
        struct iphdr *old_iph;
-       struct ipv6hdr *old_ipv6h;
        int orig_len;
        struct iphdr iph;
        struct rtable *rt;
@@ -1016,21 +1043,24 @@ gre_send(struct vport *vport, struct sk_buff *skb)
                if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
                    + sizeof(struct iphdr) - skb->data)))
                        skb->protocol = 0;
-       } else if (skb->protocol == htons(ETH_P_IPV6)) {
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else if (skb->protocol == htons(ETH_P_IPV6)) {
                if (unlikely(!pskb_may_pull(skb, skb_network_header(skb)
                    + sizeof(struct ipv6hdr) - skb->data)))
                        skb->protocol = 0;
        }
-
+#endif
        old_iph = ip_hdr(skb);
-       old_ipv6h = ipv6_hdr(skb);
 
        iph.tos = mutable->port_config.tos;
        if (mutable->port_config.flags & GRE_F_TOS_INHERIT) {
                if (skb->protocol == htons(ETH_P_IP))
                        iph.tos = old_iph->tos;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                else if (skb->protocol == htons(ETH_P_IPV6))
                        iph.tos = ipv6_get_dsfield(ipv6_hdr(skb));
+#endif
        }
        iph.tos = ecn_encapsulate(iph.tos, skb);
 
@@ -1049,8 +1079,10 @@ gre_send(struct vport *vport, struct sk_buff *skb)
        if (mutable->port_config.flags & GRE_F_TTL_INHERIT) {
                if (skb->protocol == htons(ETH_P_IP))
                        iph.ttl = old_iph->ttl;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                else if (skb->protocol == htons(ETH_P_IPV6))
-                       iph.ttl = old_ipv6h->hop_limit;
+                       iph.ttl = ipv6_hdr(skb)->hop_limit;
+#endif
        }
        if (!iph.ttl)
                iph.ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
@@ -1067,9 +1099,11 @@ gre_send(struct vport *vport, struct sk_buff *skb)
        if (skb->protocol == htons(ETH_P_IP)) {
                iph.frag_off |= old_iph->frag_off & htons(IP_DF);
                mtu = max(mtu, IP_MIN_MTU);
-
-       } else if (skb->protocol == htons(ETH_P_IPV6))
+       }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       else if (skb->protocol == htons(ETH_P_IPV6))
                mtu = max(mtu, IPV6_MIN_MTU);
+#endif
 
        iph.version = 4;
        iph.ihl = sizeof(struct iphdr) >> 2;