From 0fd0d0834f79d6cfe8a0eccc19732bae365aa575 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 1 Dec 2011 16:09:05 -0800 Subject: [PATCH] datapath: Remove custom version of ipv6_skip_exthdr(). We currently have a version of ipv6_skip_exthdr() which is identical to the main one with the addition of fragment reporting. We can propose our version for upstream and then use it directly without duplication. Signed-off-by: Jesse Gross Acked-by: Ben Pfaff --- datapath/flow.c | 70 +++------------------- datapath/linux/Modules.mk | 1 + datapath/linux/compat/exthdrs_core.c | 48 +++++++++++++++ datapath/linux/compat/include/linux/ipv6.h | 8 +++ datapath/tunnel.c | 3 +- lib/flow.c | 13 ++-- 6 files changed, 76 insertions(+), 67 deletions(-) create mode 100644 datapath/linux/compat/exthdrs_core.c diff --git a/datapath/flow.c b/datapath/flow.c index c6f591afe..78dea3a66 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -128,66 +128,6 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies) (offsetof(struct sw_flow_key, field) + \ FIELD_SIZEOF(struct sw_flow_key, field)) -/** - * skip_exthdr - skip any IPv6 extension headers - * @skb: skbuff to parse - * @start: offset of first extension header - * @nexthdrp: Initially, points to the type of the extension header at @start. - * This function updates it to point to the extension header at the final - * offset. - * @frag: Points to the @frag member in a &struct sw_flow_key. This - * function sets an appropriate %OVS_FRAG_TYPE_* value. - * - * This is based on ipv6_skip_exthdr() but adds the updates to *@frag. - * - * When there is more than one fragment header, this version reports whether - * the final fragment header that it examines is a first fragment. - * - * Returns the final payload offset, or -1 on error. - */ -static int skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, - u8 *frag) -{ - u8 nexthdr = *nexthdrp; - - while (ipv6_ext_hdr(nexthdr)) { - struct ipv6_opt_hdr _hdr, *hp; - int hdrlen; - - if (nexthdr == NEXTHDR_NONE) - return -1; - hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); - if (hp == NULL) - return -1; - if (nexthdr == NEXTHDR_FRAGMENT) { - __be16 _frag_off, *fp; - fp = skb_header_pointer(skb, - start+offsetof(struct frag_hdr, - frag_off), - sizeof(_frag_off), - &_frag_off); - if (fp == NULL) - return -1; - - if (ntohs(*fp) & ~0x7) { - *frag = OVS_FRAG_TYPE_LATER; - break; - } - *frag = OVS_FRAG_TYPE_FIRST; - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) - hdrlen = (hp->hdrlen+2)<<2; - else - hdrlen = ipv6_optlen(hp); - - nexthdr = hp->nexthdr; - start += hdrlen; - } - - *nexthdrp = nexthdr; - return start; -} - static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, int *key_lenp) { @@ -196,6 +136,7 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, int payload_ofs; struct ipv6hdr *nh; uint8_t nexthdr; + __be16 frag_off; int err; *key_lenp = SW_FLOW_KEY_OFFSET(ipv6.label); @@ -215,10 +156,17 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, key->ipv6.addr.src = nh->saddr; key->ipv6.addr.dst = nh->daddr; - payload_ofs = skip_exthdr(skb, payload_ofs, &nexthdr, &key->ip.frag); + payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr, &frag_off); if (unlikely(payload_ofs < 0)) return -EINVAL; + if (frag_off) { + if (frag_off & htons(~0x7)) + key->ip.frag = OVS_FRAG_TYPE_LATER; + else + key->ip.frag = OVS_FRAG_TYPE_FIRST; + } + nh_len = payload_ofs - nh_ofs; skb_set_transport_header(skb, nh_ofs + nh_len); key->ip.proto = nexthdr; diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index 1f9973b47..bb8eff6cf 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -1,6 +1,7 @@ openvswitch_sources += \ linux/compat/addrconf_core-openvswitch.c \ linux/compat/dev-openvswitch.c \ + linux/compat/exthdrs_core.c \ linux/compat/flex_array.c \ linux/compat/genetlink-openvswitch.c \ linux/compat/ip_output-openvswitch.c \ diff --git a/datapath/linux/compat/exthdrs_core.c b/datapath/linux/compat/exthdrs_core.c new file mode 100644 index 000000000..658e16ac6 --- /dev/null +++ b/datapath/linux/compat/exthdrs_core.c @@ -0,0 +1,48 @@ +#include +#include + +/* This function is upstream but not the version which supplies the + * fragment offset. We plan to propose the extended version. + */ +int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start, + u8 *nexthdrp, __be16 *frag_offp) +{ + u8 nexthdr = *nexthdrp; + + *frag_offp = 0; + + while (ipv6_ext_hdr(nexthdr)) { + struct ipv6_opt_hdr _hdr, *hp; + int hdrlen; + + if (nexthdr == NEXTHDR_NONE) + return -1; + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (hp == NULL) + return -1; + if (nexthdr == NEXTHDR_FRAGMENT) { + __be16 _frag_off, *fp; + fp = skb_header_pointer(skb, + start+offsetof(struct frag_hdr, + frag_off), + sizeof(_frag_off), + &_frag_off); + if (fp == NULL) + return -1; + + *frag_offp = *fp; + if (ntohs(*frag_offp) & ~0x7) + break; + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) + hdrlen = (hp->hdrlen+2)<<2; + else + hdrlen = ipv6_optlen(hp); + + nexthdr = hp->nexthdr; + start += hdrlen; + } + + *nexthdrp = nexthdr; + return start; +} diff --git a/datapath/linux/compat/include/linux/ipv6.h b/datapath/linux/compat/include/linux/ipv6.h index 25a5431af..fdbbe62a5 100644 --- a/datapath/linux/compat/include/linux/ipv6.h +++ b/datapath/linux/compat/include/linux/ipv6.h @@ -2,6 +2,7 @@ #define __LINUX_IPV6_WRAPPER_H 1 #include_next +#include #ifndef HAVE_SKBUFF_HEADER_HELPERS static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) @@ -10,4 +11,11 @@ static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) } #endif +/* This function is upstream but not the version which supplies the + * fragment offset. We plan to propose the extended version. + */ +#define ipv6_skip_exthdr rpl_ipv6_skip_exthdr +extern int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start, + u8 *nexthdrp, __be16 *frag_offp); + #endif diff --git a/datapath/tunnel.c b/datapath/tunnel.c index 4ce830ff6..33d2fe934 100644 --- a/datapath/tunnel.c +++ b/datapath/tunnel.c @@ -538,6 +538,7 @@ static bool ipv6_should_icmp(struct sk_buff *skb) int addr_type; int payload_off = (u8 *)(old_ipv6h + 1) - skb->data; u8 nexthdr = ipv6_hdr(skb)->nexthdr; + __be16 frag_off; /* Check source address is valid. */ addr_type = ipv6_addr_type(&old_ipv6h->saddr); @@ -549,7 +550,7 @@ static bool ipv6_should_icmp(struct sk_buff *skb) return false; /* Don't respond to ICMP error messages. */ - payload_off = ipv6_skip_exthdr(skb, payload_off, &nexthdr); + payload_off = ipv6_skip_exthdr(skb, payload_off, &nexthdr, &frag_off); if (payload_off < 0) return false; diff --git a/lib/flow.c b/lib/flow.c index 3865e509d..a491afffc 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -203,11 +203,14 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow) } /* We only process the first fragment. */ - flow->nw_frag = FLOW_NW_FRAG_ANY; - if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) { - flow->nw_frag |= FLOW_NW_FRAG_LATER; - nexthdr = IPPROTO_FRAGMENT; - break; + if (frag_hdr->ip6f_offlg != htons(0)) { + if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) == htons(0)) { + flow->nw_frag = FLOW_NW_FRAG_ANY; + } else { + flow->nw_frag |= FLOW_NW_FRAG_LATER; + nexthdr = IPPROTO_FRAGMENT; + break; + } } } } -- 2.43.0