(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)
{
int payload_ofs;
struct ipv6hdr *nh;
uint8_t nexthdr;
+ __be16 frag_off;
int err;
*key_lenp = SW_FLOW_KEY_OFFSET(ipv6.label);
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;
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 \
--- /dev/null
+#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;
+}
#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)
}
#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
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);
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;
}
/* 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;
+ }
}
}
}