ipv6: Add fragment reporting to ipv6_skip_exthdr().
authorJesse Gross <jesse@nicira.com>
Thu, 1 Dec 2011 01:05:51 +0000 (17:05 -0800)
committerJesse Gross <jesse@nicira.com>
Sat, 3 Dec 2011 17:35:10 +0000 (09:35 -0800)
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>
17 files changed:
include/net/ipv6.h
net/bridge/br_multicast.c
net/bridge/netfilter/ebt_ip6.c
net/bridge/netfilter/ebt_log.c
net/ipv6/exthdrs_core.c
net/ipv6/icmp.c
net/ipv6/ip6_input.c
net/ipv6/ip6_output.c
net/ipv6/netfilter/ip6t_REJECT.c
net/netfilter/ipset/ip_set_getport.c
net/netfilter/xt_AUDIT.c
net/netfilter/xt_TCPMSS.c
net/netfilter/xt_TCPOPTSTRIP.c
net/netfilter/xt_hashlimit.c
net/netfilter/xt_socket.c
security/lsm_audit.c
security/selinux/hooks.c

index f35188e002d92a270752a1c4aee02d735d8ce920..e4170a22fc6f12325d6757f07cf7430d1fbcbff3 100644 (file)
@@ -558,7 +558,7 @@ extern void                 ipv6_push_frag_opts(struct sk_buff *skb,
                                                    u8 *proto);
 
 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);
 
index 7743e0d109ea20b0b5c56445f83b1a1d168ac0ad..375417e633c94330ffe74aafa8b69bb990ff597c 100644 (file)
@@ -1458,6 +1458,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
        const struct ipv6hdr *ip6h;
        u8 icmp6_type;
        u8 nexthdr;
+       __be16 frag_off;
        unsigned len;
        int offset;
        int err;
@@ -1483,7 +1484,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
                return -EINVAL;
 
        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)
                return 0;
index 2ed0056a39a88afb27beac4fc0e57c3c00bae347..99c85668f5518ab9ee8d05a7b085139734893fdd 100644 (file)
@@ -55,9 +55,10 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
                return false;
        if (info->bitmask & EBT_IP6_PROTO) {
                uint8_t nexthdr = ih6->nexthdr;
+               __be16 frag_off;
                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)
                        return false;
                if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
index 6e5a8bb9b940ce3eb03e1dc6840f4e84ef4d1f98..88d7d1d1cb1bd1df8b8a0d57eca4b8de35c6cd02 100644 (file)
@@ -113,6 +113,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
                const struct ipv6hdr *ih;
                struct ipv6hdr _iph;
                uint8_t nexthdr;
+               __be16 frag_off;
                int offset_ph;
 
                ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
@@ -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",
                       &ih->saddr, &ih->daddr, ih->priority, 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)
                        goto out;
                print_ports(skb, nexthdr, offset_ph);
index 37f548b7f6dc73c4489107a59ee48e4928efa8f1..72957f4a7c6c9f6c24056a5c3eb6046b61c7a3ea 100644 (file)
@@ -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)
index 9e2bdccf9143e826799274a55656f9ffc21c2c6f..01d46bff63c312b7804c8132d6860a1a749eb043 100644 (file)
@@ -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 {
index a46c64eb0a660b47b132e4b2346e1c112cc05759..1ca5d45a12e8973408ececb5ebd4e1daa3e391f9 100644 (file)
@@ -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;
 
index a24e15557843521940693bb7f42033664e0ea69d..3221bc675654b196d90d0c96d919dc9b1f0b3e0c 100644 (file)
@@ -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
index b5a2aa58a03acf828fd0eb0247dfafef09335969..aad2fa41cf460041578f06b4eff7b6d6f3417054 100644 (file)
@@ -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");
index 052579fe389ab774b0d7fa704d1c7b371fc96b8f..b71a6e7ab0a5a188ec10b4f4ab79977205029a35 100644 (file)
@@ -116,9 +116,11 @@ ip_set_get_ip6_port(const struct sk_buff *skb, bool src,
 {
        int protoff;
        u8 nexthdr;
+       __be16 frag_off;
 
        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)
                return false;
 
index 4bca15a0c38547ed7701684034733fe009163df0..ba92824086f3c3843c7158c5a5de86fe0db53dc4 100644 (file)
@@ -98,6 +98,7 @@ static void audit_ip6(struct audit_buffer *ab, struct sk_buff *skb)
        struct ipv6hdr _ip6h;
        const struct ipv6hdr *ih;
        u8 nexthdr;
+       __be16 frag_off;
        int offset;
 
        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)
 
        nexthdr = ih->nexthdr;
        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",
                         &ih->saddr, &ih->daddr, nexthdr);
index 3ecade3966d5f7474704b705ff2f08614299b5f1..ba722621ed252f1d25857f272a33e46163f328d9 100644 (file)
@@ -204,11 +204,12 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
 {
        struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        u8 nexthdr;
+       __be16 frag_off;
        int tcphoff;
        int ret;
 
        nexthdr = ipv6h->nexthdr;
-       tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr);
+       tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
        if (tcphoff < 0)
                return NF_DROP;
        ret = tcpmss_mangle_packet(skb, par->targinfo,
index 9dc9ecfdd546298e4bc6dca11a20844367562416..3a295cc734bdacbf4b149b36f078d220cb82e5f8 100644 (file)
@@ -87,9 +87,10 @@ tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par)
        struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        int tcphoff;
        u_int8_t nexthdr;
+       __be16 frag_off;
 
        nexthdr = ipv6h->nexthdr;
-       tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr);
+       tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
        if (tcphoff < 0)
                return NF_DROP;
 
index dfd52bad1523f73535110e0d1b2c9f4a87c6545a..068698f647912e1245819084d3806147fa345f50 100644 (file)
@@ -445,6 +445,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
 {
        __be16 _ports[2], *ports;
        u8 nexthdr;
+       __be16 frag_off;
        int poff;
 
        memset(dst, 0, sizeof(*dst));
@@ -480,7 +481,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
                      (XT_HASHLIMIT_HASH_DPT | XT_HASHLIMIT_HASH_SPT)))
                        return 0;
                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)
                        return -1;
                break;
index fe39f7e913dff490e948ca47b1ce6c14844013d6..c302e30dc50c2951e2e1c54812c38eda1e14af4b 100644 (file)
@@ -214,6 +214,7 @@ extract_icmp6_fields(const struct sk_buff *skb,
        struct icmp6hdr *icmph, _icmph;
        __be16 *ports, _ports[2];
        u8 inside_nexthdr;
+       __be16 inside_fragoff;
        int inside_hdrlen;
 
        icmph = skb_header_pointer(skb, outside_hdrlen,
@@ -229,7 +230,8 @@ extract_icmp6_fields(const struct sk_buff *skb,
                return 1;
        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)
                return 1; /* hjm: Packet has no/incomplete transport layer headers. */
 
index 199616bb68d32839fcc4e301686cd64b21bb4269..7bd6f138236b3a010d457ab473ea73fd02641c8c 100644 (file)
@@ -114,6 +114,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
        int offset, ret = 0;
        struct ipv6hdr *ip6;
        u8 nexthdr;
+       __be16 frag_off;
 
        ip6 = ipv6_hdr(skb);
        if (ip6 == NULL)
@@ -126,7 +127,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
        offset = skb_network_offset(skb);
        offset += sizeof(*ip6);
        nexthdr = ip6->nexthdr;
-       offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
+       offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
        if (offset < 0)
                return 0;
        if (proto)
index 7e6c2564e741ba496ce21589b7740be638cb3a01..cca09bb4650202af6cecc17c0741eee01e899d6a 100644 (file)
@@ -3561,6 +3561,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
        u8 nexthdr;
        int ret = -EINVAL, offset;
        struct ipv6hdr _ipv6h, *ip6;
+       __be16 frag_off;
 
        offset = skb_network_offset(skb);
        ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
@@ -3573,7 +3574,7 @@ static int selinux_parse_skb_ipv6(struct sk_buff *skb,
 
        nexthdr = ip6->nexthdr;
        offset += sizeof(_ipv6h);
-       offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
+       offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
        if (offset < 0)
                goto out;