[ovs-dev] [PATCH 1/2] datapath: Add ipv6_find_hdr() compatibility function.

Ansis Atteka aatteka at nicira.com
Tue Nov 6 15:25:21 UTC 2012


Implementation of the IPv6 'set' action depends on ipv6_find_exthdr()
function to find routing header. By looking at the routing headers,
it is possible to tell, whether L4 checksums will need to be
recalculated, whenever the destination address is changed in
the main IPv6 header.

This function will need to be customized so that it would skip all
routing headers, where segements_left is equal to 0.

Signed-off-by: Ansis Atteka <aatteka at nicira.com>
---
 datapath/linux/compat/exthdrs_core.c     |  102 ++++++++++++++++++++++++++++++
 datapath/linux/compat/include/net/ipv6.h |   11 ++++
 2 files changed, 113 insertions(+)

diff --git a/datapath/linux/compat/exthdrs_core.c b/datapath/linux/compat/exthdrs_core.c
index 658e16a..ca5597f 100644
--- a/datapath/linux/compat/exthdrs_core.c
+++ b/datapath/linux/compat/exthdrs_core.c
@@ -46,3 +46,105 @@ int rpl_ipv6_skip_exthdr(const struct sk_buff *skb, int start,
 	*nexthdrp = nexthdr;
 	return start;
 }
+
+/*
+ * find the offset to specified header or the protocol number of last header
+ * if target < 0. "last header" is transport protocol header, ESP, or
+ * "No next header".
+ *
+ * Note that *offset is used as input/output parameter. an if it is not zero,
+ * then it must be a valid offset to an inner IPv6 header. This can be used
+ * to explore inner IPv6 header, eg. ICMPv6 error messages.
+ *
+ * If target header is found, its offset is set in *offset and return protocol
+ * number. Otherwise, return -1.
+ *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
+ * Note that non-1st fragment is special case that "the protocol number
+ * of last header" is "next header" field in Fragment header. In this case,
+ * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
+ * isn't NULL.
+ *
+ * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG
+ * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and
+ * target < 0, then this function will stop at the AH header.
+ */
+int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
+		  int target, unsigned short *fragoff, int *flags)
+{
+	unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
+	u8 nexthdr = ipv6_hdr(skb)->nexthdr;
+	unsigned int len;
+
+	if (fragoff)
+		*fragoff = 0;
+
+	if (*offset) {
+		struct ipv6hdr _ip6, *ip6;
+
+		ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
+		if (!ip6 || (ip6->version != 6)) {
+			printk(KERN_ERR "IPv6 header not found\n");
+			return -EBADMSG;
+		}
+		start = *offset + sizeof(struct ipv6hdr);
+		nexthdr = ip6->nexthdr;
+	}
+	len = skb->len - start;
+
+	while (nexthdr != target) {
+		struct ipv6_opt_hdr _hdr, *hp;
+		unsigned int hdrlen;
+
+		if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
+			if (target < 0)
+				break;
+			return -ENOENT;
+		}
+
+		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
+		if (hp == NULL)
+			return -EBADMSG;
+		if (nexthdr == NEXTHDR_FRAGMENT) {
+			unsigned short _frag_off;
+			__be16 *fp;
+
+			if (flags)	/* Indicate that this is a fragment */
+				*flags |= IP6T_FH_F_FRAG;
+			fp = skb_header_pointer(skb,
+						start+offsetof(struct frag_hdr,
+							       frag_off),
+						sizeof(_frag_off),
+						&_frag_off);
+			if (fp == NULL)
+				return -EBADMSG;
+
+			_frag_off = ntohs(*fp) & ~0x7;
+			if (_frag_off) {
+				if (target < 0 &&
+				    ((!ipv6_ext_hdr(hp->nexthdr)) ||
+				     hp->nexthdr == NEXTHDR_NONE)) {
+					if (fragoff)
+						*fragoff = _frag_off;
+					return hp->nexthdr;
+				}
+				return -ENOENT;
+			}
+			hdrlen = 8;
+		} else if (nexthdr == NEXTHDR_AUTH) {
+			if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0))
+				break;
+			hdrlen = (hp->hdrlen + 2) << 2;
+		} else
+			hdrlen = ipv6_optlen(hp);
+
+		nexthdr = hp->nexthdr;
+		len -= hdrlen;
+		start += hdrlen;
+	}
+
+	*offset = start;
+	return nexthdr;
+}
diff --git a/datapath/linux/compat/include/net/ipv6.h b/datapath/linux/compat/include/net/ipv6.h
index 8bb7d65..0b82ec2 100644
--- a/datapath/linux/compat/include/net/ipv6.h
+++ b/datapath/linux/compat/include/net/ipv6.h
@@ -3,6 +3,11 @@
 
 #include_next <net/ipv6.h>
 
+enum {
+	IP6T_FH_F_FRAG	= (1 << 0),
+	IP6T_FH_F_AUTH	= (1 << 1),
+};
+
 /* This function is upstream but not the version which supplies the
  * fragment offset.  We plan to propose the extended version.
  */
@@ -10,4 +15,10 @@
 extern int ipv6_skip_exthdr(const struct sk_buff *skb, int start,
 				u8 *nexthdrp, __be16 *frag_offp);
 
+/* This function is upstream, but not the version which skips routing
+ * headers with 0 segments_left. We plan to propose the extended version. */
+#define ipv6_find_hdr rpl_ipv6_find_hdr
+extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
+			 int target, unsigned short *fragoff, int *fragflg);
+
 #endif
-- 
1.7.9.5




More information about the dev mailing list