datapath: Remove custom version of ipv6_skip_exthdr().
authorJesse Gross <jesse@nicira.com>
Fri, 2 Dec 2011 00:09:05 +0000 (16:09 -0800)
committerJesse Gross <jesse@nicira.com>
Fri, 2 Dec 2011 19:22:13 +0000 (11:22 -0800)
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 <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
datapath/flow.c
datapath/linux/Modules.mk
datapath/linux/compat/exthdrs_core.c [new file with mode: 0644]
datapath/linux/compat/include/linux/ipv6.h
datapath/tunnel.c
lib/flow.c

index c6f591a..78dea3a 100644 (file)
@@ -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;
index 1f9973b..bb8eff6 100644 (file)
@@ -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 (file)
index 0000000..658e16a
--- /dev/null
@@ -0,0 +1,48 @@
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+
+/* 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;
+}
index 25a5431..fdbbe62 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_IPV6_WRAPPER_H 1
 
 #include_next <linux/ipv6.h>
+#include <net/ipv6.h>
 
 #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
index 4ce830f..33d2fe9 100644 (file)
@@ -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;
 
index 3865e50..a491aff 100644 (file)
@@ -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;
+                }
             }
         }
     }