From: Ansis Atteka Date: Tue, 6 Nov 2012 11:39:58 +0000 (+0200) Subject: datapath: Add ipv6_find_hdr() compatibility function. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=openvswitch;a=commitdiff_plain;h=1007d8acad106fb508ccfdb84ca5bbdc491efc6c datapath: Add ipv6_find_hdr() compatibility function. 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 --- diff --git a/datapath/linux/compat/exthdrs_core.c b/datapath/linux/compat/exthdrs_core.c index 658e16ac..1749c066 100644 --- a/datapath/linux/compat/exthdrs_core.c +++ b/datapath/linux/compat/exthdrs_core.c @@ -46,3 +46,107 @@ 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 + * OVS_IP6T_FH_F_FRAG will be set. If it's an AH header, the + * OVS_IP6T_FH_F_AUTH flag is set and target < 0, then this function will + * stop at the AH header. + */ +int rpl_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 |= OVS_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 & OVS_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 8bb7d656..9ba85594 100644 --- a/datapath/linux/compat/include/net/ipv6.h +++ b/datapath/linux/compat/include/net/ipv6.h @@ -3,6 +3,11 @@ #include_next +enum { + OVS_IP6T_FH_F_FRAG = (1 << 0), + OVS_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