Commit 75f2811c authored by Jesse Gross's avatar Jesse Gross

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: default avatarJesse Gross <jesse@nicira.com>
parent 396cf943
...@@ -558,7 +558,7 @@ extern void ipv6_push_frag_opts(struct sk_buff *skb, ...@@ -558,7 +558,7 @@ extern void ipv6_push_frag_opts(struct sk_buff *skb,
u8 *proto); u8 *proto);
extern int ipv6_skip_exthdr(const struct sk_buff *, int start, extern int ipv6_skip_exthdr(const struct sk_buff *, int start,
u8 *nexthdrp); u8 *nexthdrp, __be16 *frag_offp);
extern int ipv6_ext_hdr(u8 nexthdr); extern int ipv6_ext_hdr(u8 nexthdr);
......
...@@ -1458,6 +1458,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1458,6 +1458,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
const struct ipv6hdr *ip6h; const struct ipv6hdr *ip6h;
u8 icmp6_type; u8 icmp6_type;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
unsigned len; unsigned len;
int offset; int offset;
int err; int err;
...@@ -1483,7 +1484,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, ...@@ -1483,7 +1484,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
return -EINVAL; return -EINVAL;
nexthdr = ip6h->nexthdr; nexthdr = ip6h->nexthdr;
offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr); offset = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nexthdr, &frag_off);
if (offset < 0 || nexthdr != IPPROTO_ICMPV6) if (offset < 0 || nexthdr != IPPROTO_ICMPV6)
return 0; return 0;
......
...@@ -55,9 +55,10 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) ...@@ -55,9 +55,10 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
return false; return false;
if (info->bitmask & EBT_IP6_PROTO) { if (info->bitmask & EBT_IP6_PROTO) {
uint8_t nexthdr = ih6->nexthdr; uint8_t nexthdr = ih6->nexthdr;
__be16 frag_off;
int offset_ph; int offset_ph;
offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr); offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr, &frag_off);
if (offset_ph == -1) if (offset_ph == -1)
return false; return false;
if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO)) if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
......
...@@ -113,6 +113,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum, ...@@ -113,6 +113,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
const struct ipv6hdr *ih; const struct ipv6hdr *ih;
struct ipv6hdr _iph; struct ipv6hdr _iph;
uint8_t nexthdr; uint8_t nexthdr;
__be16 frag_off;
int offset_ph; int offset_ph;
ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
...@@ -123,7 +124,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum, ...@@ -123,7 +124,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
printk(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d", printk(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d",
&ih->saddr, &ih->daddr, ih->priority, ih->nexthdr); &ih->saddr, &ih->daddr, ih->priority, ih->nexthdr);
nexthdr = ih->nexthdr; nexthdr = ih->nexthdr;
offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr); offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr, &frag_off);
if (offset_ph == -1) if (offset_ph == -1)
goto out; goto out;
print_ports(skb, nexthdr, offset_ph); print_ports(skb, nexthdr, offset_ph);
......
...@@ -57,6 +57,9 @@ int ipv6_ext_hdr(u8 nexthdr) ...@@ -57,6 +57,9 @@ int ipv6_ext_hdr(u8 nexthdr)
* it returns NULL. * it returns NULL.
* - First fragment header is skipped, not-first ones * - First fragment header is skipped, not-first ones
* are considered as unparsable. * 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 * - ESP is unparsable for now and considered like
* normal payload protocol. * normal payload protocol.
* - Note also special handling of AUTH header. Thanks to IPsec wizards. * - Note also special handling of AUTH header. Thanks to IPsec wizards.
...@@ -64,10 +67,13 @@ int ipv6_ext_hdr(u8 nexthdr) ...@@ -64,10 +67,13 @@ int ipv6_ext_hdr(u8 nexthdr)
* --ANK (980726) * --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; u8 nexthdr = *nexthdrp;
*frag_offp = 0;
while (ipv6_ext_hdr(nexthdr)) { while (ipv6_ext_hdr(nexthdr)) {
struct ipv6_opt_hdr _hdr, *hp; struct ipv6_opt_hdr _hdr, *hp;
int hdrlen; int hdrlen;
...@@ -87,7 +93,8 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp) ...@@ -87,7 +93,8 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp)
if (fp == NULL) if (fp == NULL)
return -1; return -1;
if (ntohs(*fp) & ~0x7) *frag_offp = *fp;
if (ntohs(*frag_offp) & ~0x7)
break; break;
hdrlen = 8; hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH) } else if (nexthdr == NEXTHDR_AUTH)
......
...@@ -135,11 +135,12 @@ static int is_ineligible(struct sk_buff *skb) ...@@ -135,11 +135,12 @@ static int is_ineligible(struct sk_buff *skb)
int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data; int ptr = (u8 *)(ipv6_hdr(skb) + 1) - skb->data;
int len = skb->len - ptr; int len = skb->len - ptr;
__u8 nexthdr = ipv6_hdr(skb)->nexthdr; __u8 nexthdr = ipv6_hdr(skb)->nexthdr;
__be16 frag_off;
if (len < 0) if (len < 0)
return 1; return 1;
ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr); ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, &frag_off);
if (ptr < 0) if (ptr < 0)
return 0; return 0;
if (nexthdr == IPPROTO_ICMPV6) { if (nexthdr == IPPROTO_ICMPV6) {
...@@ -596,6 +597,7 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) ...@@ -596,6 +597,7 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
int inner_offset; int inner_offset;
int hash; int hash;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
return; return;
...@@ -603,7 +605,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) ...@@ -603,7 +605,8 @@ static void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr; nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;
if (ipv6_ext_hdr(nexthdr)) { if (ipv6_ext_hdr(nexthdr)) {
/* now skip over extension headers */ /* 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) if (inner_offset<0)
return; return;
} else { } else {
......
...@@ -280,6 +280,7 @@ int ip6_mc_input(struct sk_buff *skb) ...@@ -280,6 +280,7 @@ int ip6_mc_input(struct sk_buff *skb)
u8 *ptr = skb_network_header(skb) + opt->ra; u8 *ptr = skb_network_header(skb) + opt->ra;
struct icmp6hdr *icmp6; struct icmp6hdr *icmp6;
u8 nexthdr = hdr->nexthdr; u8 nexthdr = hdr->nexthdr;
__be16 frag_off;
int offset; int offset;
/* Check if the value of Router Alert /* Check if the value of Router Alert
...@@ -293,7 +294,7 @@ int ip6_mc_input(struct sk_buff *skb) ...@@ -293,7 +294,7 @@ int ip6_mc_input(struct sk_buff *skb)
goto out; goto out;
} }
offset = ipv6_skip_exthdr(skb, sizeof(*hdr), offset = ipv6_skip_exthdr(skb, sizeof(*hdr),
&nexthdr); &nexthdr, &frag_off);
if (offset < 0) if (offset < 0)
goto out; goto out;
......
...@@ -329,10 +329,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) ...@@ -329,10 +329,11 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
{ {
struct ipv6hdr *hdr = ipv6_hdr(skb); struct ipv6hdr *hdr = ipv6_hdr(skb);
u8 nexthdr = hdr->nexthdr; u8 nexthdr = hdr->nexthdr;
__be16 frag_off;
int offset; int offset;
if (ipv6_ext_hdr(nexthdr)) { 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) if (offset < 0)
return 0; return 0;
} else } else
......
...@@ -49,6 +49,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) ...@@ -49,6 +49,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
const __u8 tclass = DEFAULT_TOS_VALUE; const __u8 tclass = DEFAULT_TOS_VALUE;
struct dst_entry *dst = NULL; struct dst_entry *dst = NULL;
u8 proto; u8 proto;
__be16 frag_off;
struct flowi6 fl6; struct flowi6 fl6;
if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
...@@ -58,7 +59,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) ...@@ -58,7 +59,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
} }
proto = oip6h->nexthdr; 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)) { if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
pr_debug("Cannot get TCP header.\n"); pr_debug("Cannot get TCP header.\n");
......
...@@ -116,9 +116,11 @@ ip_set_get_ip6_port(const struct sk_buff *skb, bool src, ...@@ -116,9 +116,11 @@ ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
{ {
int protoff; int protoff;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
nexthdr = ipv6_hdr(skb)->nexthdr; nexthdr = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
&frag_off);
if (protoff < 0) if (protoff < 0)
return false; return false;
......
...@@ -98,6 +98,7 @@ static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) ...@@ -98,6 +98,7 @@ static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
struct ipv6hdr _ip6h; struct ipv6hdr _ip6h;
const struct ipv6hdr *ih; const struct ipv6hdr *ih;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
int offset; int offset;
ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h); ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h);
...@@ -108,7 +109,7 @@ static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) ...@@ -108,7 +109,7 @@ static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
nexthdr = ih->nexthdr; nexthdr = ih->nexthdr;
offset = ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), offset = ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h),
&nexthdr); &nexthdr, &frag_off);
audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu",
&ih->saddr, &ih->daddr, nexthdr); &ih->saddr, &ih->daddr, nexthdr);
......
...@@ -204,11 +204,12 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -204,11 +204,12 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{ {
struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6hdr *ipv6h = ipv6_hdr(skb);
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
int tcphoff; int tcphoff;
int ret; int ret;
nexthdr = ipv6h->nexthdr; nexthdr = ipv6h->nexthdr;
tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
if (tcphoff < 0) if (tcphoff < 0)
return NF_DROP; return NF_DROP;
ret = tcpmss_mangle_packet(skb, par->targinfo, ret = tcpmss_mangle_packet(skb, par->targinfo,
......
...@@ -87,9 +87,10 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par) ...@@ -87,9 +87,10 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par)
struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6hdr *ipv6h = ipv6_hdr(skb);
int tcphoff; int tcphoff;
u_int8_t nexthdr; u_int8_t nexthdr;
__be16 frag_off;
nexthdr = ipv6h->nexthdr; nexthdr = ipv6h->nexthdr;
tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
if (tcphoff < 0) if (tcphoff < 0)
return NF_DROP; return NF_DROP;
......
...@@ -445,6 +445,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, ...@@ -445,6 +445,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
{ {
__be16 _ports[2], *ports; __be16 _ports[2], *ports;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
int poff; int poff;
memset(dst, 0, sizeof(*dst)); memset(dst, 0, sizeof(*dst));
...@@ -480,7 +481,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, ...@@ -480,7 +481,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
(XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT))) (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT)))
return 0; return 0;
nexthdr = ipv6_hdr(skb)->nexthdr; nexthdr = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr); protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr, &frag_off);
if ((int)protoff < 0) if ((int)protoff < 0)
return -1; return -1;
break; break;
......
...@@ -214,6 +214,7 @@ extract_icmp6_fields(const struct sk_buff *skb, ...@@ -214,6 +214,7 @@ extract_icmp6_fields(const struct sk_buff *skb,
struct icmp6hdr *icmph, _icmph; struct icmp6hdr *icmph, _icmph;
__be16 *ports, _ports[2]; __be16 *ports, _ports[2];
u8 inside_nexthdr; u8 inside_nexthdr;
__be16 inside_fragoff;
int inside_hdrlen; int inside_hdrlen;
icmph = skb_header_pointer(skb, outside_hdrlen, icmph = skb_header_pointer(skb, outside_hdrlen,
...@@ -229,7 +230,8 @@ extract_icmp6_fields(const struct sk_buff *skb, ...@@ -229,7 +230,8 @@ extract_icmp6_fields(const struct sk_buff *skb,
return 1; return 1;
inside_nexthdr = inside_iph->nexthdr; inside_nexthdr = inside_iph->nexthdr;
inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), &inside_nexthdr); inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph),
&inside_nexthdr, &inside_fragoff);
if (inside_hdrlen < 0) if (inside_hdrlen < 0)
return 1; /* hjm: Packet has no/incomplete transport layer headers. */ return 1; /* hjm: Packet has no/incomplete transport layer headers. */
......
...@@ -114,6 +114,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, ...@@ -114,6 +114,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
int offset, ret = 0; int offset, ret = 0;
struct ipv6hdr *ip6; struct ipv6hdr *ip6;
u8 nexthdr; u8 nexthdr;
__be16 frag_off;
ip6 = ipv6_hdr(skb); ip6 = ipv6_hdr(skb);
if (ip6 == NULL) if (ip6 == NULL)
...@@ -126,7 +127,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb, ...@@ -126,7 +127,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
offset = skb_network_offset(skb); offset = skb_network_offset(skb);
offset += sizeof(*ip6); offset += sizeof(*ip6);
nexthdr = ip6->nexthdr; nexthdr = ip6->nexthdr;
offset = ipv6_skip_exthdr(skb, offset, &nexthdr); offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
if (offset < 0) if (offset < 0)
return 0; return 0;
if (proto) if (proto)
......
...@@ -3561,6 +3561,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, ...@@ -3561,6 +3561,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
u8 nexthdr; u8 nexthdr;
int ret = -EINVAL, offset; int ret = -EINVAL, offset;
struct ipv6hdr _ipv6h, *ip6; struct ipv6hdr _ipv6h, *ip6;
__be16 frag_off;
offset = skb_network_offset(skb); offset = skb_network_offset(skb);
ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
...@@ -3573,7 +3574,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb, ...@@ -3573,7 +3574,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
nexthdr = ip6->nexthdr; nexthdr = ip6->nexthdr;
offset += sizeof(_ipv6h); offset += sizeof(_ipv6h);
offset = ipv6_skip_exthdr(skb, offset, &nexthdr); offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
if (offset < 0) if (offset < 0)
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment