ipv6: Add fragment reporting to ipv6_skip_exthdr().
While parsing through IPv6 extension headers, fragment headers are skipped making them invisible to the caller. This reports the fragment offset of the last header in order to make it possible to determine whether the packet is fragmented and, if so whether it is a first or last fragment. Signed-off-by: Jesse Gross <jesse@nicira.com>
This commit is contained in:
@@ -57,6 +57,9 @@ int ipv6_ext_hdr(u8 nexthdr)
|
||||
* it returns NULL.
|
||||
* - First fragment header is skipped, not-first ones
|
||||
* are considered as unparsable.
|
||||
* - Reports the offset field of the final fragment header so it is
|
||||
* possible to tell whether this is a first fragment, later fragment,
|
||||
* or not fragmented.
|
||||
* - ESP is unparsable for now and considered like
|
||||
* normal payload protocol.
|
||||
* - Note also special handling of AUTH header. Thanks to IPsec wizards.
|
||||
@@ -64,10 +67,13 @@ int ipv6_ext_hdr(u8 nexthdr)
|
||||
* --ANK (980726)
|
||||
*/
|
||||
|
||||
int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp)
|
||||
int 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;
|
||||
@@ -87,7 +93,8 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp)
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
if (ntohs(*fp) & ~0x7)
|
||||
*frag_offp = *fp;
|
||||
if (ntohs(*frag_offp) & ~0x7)
|
||||
break;
|
||||
hdrlen = 8;
|
||||
} else if (nexthdr == NEXTHDR_AUTH)
|
||||
|
@@ -135,11 +135,12 @@ static int is_ineligible(struct sk_buff *skb)
|
||||
int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
|
||||
int len = skb->len - ptr;
|
||||
__u8 nexthdr = ipv6_hdr(skb)->nexthdr;
|
||||
__be16 frag_off;
|
||||
|
||||
if (len < 0)
|
||||
return 1;
|
||||
|
||||
ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr);
|
||||
ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off);
|
||||
if (ptr < 0)
|
||||
return 0;
|
||||
if (nexthdr == IPPROTO_ICMPV6) {
|
||||
@@ -596,6 +597,7 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
|
||||
int inner_offset;
|
||||
int hash;
|
||||
u8 nexthdr;
|
||||
__be16 frag_off;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return;
|
||||
@@ -603,7 +605,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
|
||||
nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;
|
||||
if (ipv6_ext_hdr(nexthdr)) {
|
||||
/* now skip over extension headers */
|
||||
inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
|
||||
inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
|
||||
&nexthdr, &frag_off);
|
||||
if (inner_offset<0)
|
||||
return;
|
||||
} else {
|
||||
|
@@ -280,6 +280,7 @@ int ip6_mc_input(struct sk_buff *skb)
|
||||
u8 *ptr = skb_network_header(skb) + opt->ra;
|
||||
struct icmp6hdr *icmp6;
|
||||
u8 nexthdr = hdr->nexthdr;
|
||||
__be16 frag_off;
|
||||
int offset;
|
||||
|
||||
/* Check if the value of Router Alert
|
||||
@@ -293,7 +294,7 @@ int ip6_mc_input(struct sk_buff *skb)
|
||||
goto out;
|
||||
}
|
||||
offset = ipv6_skip_exthdr(skb, sizeof(*hdr),
|
||||
&nexthdr);
|
||||
&nexthdr, &frag_off);
|
||||
if (offset < 0)
|
||||
goto out;
|
||||
|
||||
|
@@ -329,10 +329,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
|
||||
{
|
||||
struct ipv6hdr *hdr = ipv6_hdr(skb);
|
||||
u8 nexthdr = hdr->nexthdr;
|
||||
__be16 frag_off;
|
||||
int offset;
|
||||
|
||||
if (ipv6_ext_hdr(nexthdr)) {
|
||||
offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr);
|
||||
offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, &frag_off);
|
||||
if (offset < 0)
|
||||
return 0;
|
||||
} else
|
||||
|
@@ -49,6 +49,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
|
||||
const __u8 tclass = DEFAULT_TOS_VALUE;
|
||||
struct dst_entry *dst = NULL;
|
||||
u8 proto;
|
||||
__be16 frag_off;
|
||||
struct flowi6 fl6;
|
||||
|
||||
if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
|
||||
@@ -58,7 +59,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
|
||||
}
|
||||
|
||||
proto = oip6h->nexthdr;
|
||||
tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto);
|
||||
tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off);
|
||||
|
||||
if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
|
||||
pr_debug("Cannot get TCP header.\n");
|
||||
|
Reference in New Issue
Block a user