From 6f4709828bed7b831015d6c22dcb209de02f7537 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 18 May 2010 17:30:42 -0700 Subject: [PATCH] gre: Check whether IPv6 is compiled into the kernel. 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 | 4 +++ datapath/vport-gre.c | 60 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/INSTALL.Linux b/INSTALL.Linux index 20a0dd80..c3cf2f84 100644 --- a/INSTALL.Linux +++ b/INSTALL.Linux @@ -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. diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c index dbfc1001..237835ba 100644 --- a/datapath/vport-gre.c +++ b/datapath/vport-gre.c @@ -22,7 +22,9 @@ #include #include #include +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) #include +#endif #include #include #include @@ -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; -- 2.30.2