netdev
[Top] [All Lists]

IPv6 fragmentation and IPv6 header parsing

To: netdev@xxxxxxxxxxx
Subject: IPv6 fragmentation and IPv6 header parsing
From: Brad Chapman <kakadu@xxxxxxxxxxxxx>
Date: Sun, 29 Jul 2001 20:55:18 -0400
Sender: owner-netdev@xxxxxxxxxxx
User-agent: Mozilla/5.0 (X11; U; Linux 2.4.7 i586; en-US; C-UPD: MaxLinux0301) Gecko/20001107 Netscape6/6.0
Everyone,

  I am currently completing a port of the Netfilter connection
tracking subsystem from IPv4 to IPv6. Most of the features in this
port are complete, except for fragment handling, which is non-
existent. I am also not entirely sure about how to properly parse
header chains and extract various extension and layer-4 headers
for use by the connection tracking subsystem. Enclosed with this
message are my current efforts regarding IPv6 fragmentation and
IPv6 header chain parsing.

  I would appreciate any feedback at all regarding this.

Thanks,

Brad

IPv6 fragmentation support
<snip>

/* Sun Jul 29 2001 - Created ip6_conntrack_fragment.c */

#include <linux/types.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/brlock.h>
#include <linux/version.h>
#include <net/checksum.h>
#include <net/ipv6.h>
#include <net/flow.h>

#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip6_conntrack_lock)
#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip6_conntrack_lock)

#include <linux/netfilter_ipv6/ip6_conntrack.h>
#include <linux/netfilter_ipv6/ip6_conntrack_core.h>
#include <linux/netfilter_ipv6/ip6_conntrack_fragment.h>
#include <linux/netfilter_ipv6/listhelp.h>

#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif

/* Initialize the flowlabel */
static void init_fl(struct sk_buff *skb, struct flowi *flow)
{
        flow->proto = (u_int8_t)get_ipv6_header((*pskb)->nh.ipv6h, 0);
        ipv6_addr_copy(flow->fl6_dst, (struct in6_addr 
*)(*pskb)->nh.ipv6h->daddr);
        ipv6_addr_copy(flow->fl6_src, (struct in6_addr 
*)(*pskb)->nh.ipv6h->saddr);
        flow->flowlabel = (*pskb)->nh.ipv6h->flow_lbl[0] +
                          (*pskb)->nh.ipv6h->flow_lbl[1] +
                          (*pskb)->nh.ipv6h->flow_lbl[2];
       switch (flow->proto)
                case IPPROTO_TCP : {
                        flow->uli_u.ports.sport = (*pskb)->h.tcph->sport;
                        flow->uli_u.ports.dport = (*pskb)->h.tcph->dport;
                }
                case IPPROTO_UDP : {
                        flow->uli_u.ports.sport = (*pskb)->h.udph->sport;
                        flow->uli_u.ports.dport = (*pskb)->h.udph->dport;
                }
                case IPPROTO_ICMPV6 : {
                        struct icmp6hdr *icmpv6h =
                         (struct icmp6hdr *)get_ipv6_header((*pskb)->nh.ipv6h, 
IPPROTO_ICMPV6);
                        flow->uli_u.icmpt.code = icmpv6h->type;
                        flow->uli_u.icmpt.type = icmpv6h->code;
                }
        flow->oif = 0;
        flow->uli_u.data = 0;
       return;
}

/* Initialize the transmission options struct */
static void init_opt(struct sk_buff *skb, struct ipv6_txoptions *opt)
{
        struct frag_hdr *fhdr = (struct frag_hdr 
*)get_ipv6_header((*pskb)->nh.ipv6h, NEXTHDR_FRAGMENT);
        u_int32_t proto = get_ipv6_header((*pskb)->nh.ipv6h, 0);

        opt->hopopt = (struct ipv6_opt_hdr *)get_ipv6_header((*pskb)->nh.ipv6h, 
NEXTHDR_HOP);
        opt->dst0opt = (struct ipv6_opt_hdr 
*)get_ipv6_header((*pskb)->nh.ipv6h, NEXTHDR_DEST);
        opt->srcrt = (struct ipv6_opt_hdr *)get_ipv6_header((*pskb)->nh.ipv6h, 
NEXTHDR_ROUTING);
        opt->auth = (struct ipv6_opt_hdr *)get_ipv6_header((*pskb)->nh.ipv6h, 
NEXTHDR_AUTH);
        opt->dst1opt = (struct ipv6_opt_hdr 
*)get_ipv6_header((*pskb)->nh.ipv6h, NEXTHDR_DEST);

        /* Hmmmm.... we have to find all headers before the fragment header and 
all after it;
                a lot of this is duplicated from get_ipv6_header() - kisza 
_has_ to see this
                and tell me what's wrong -- BC */
        opt->opt_flen += (u_int16_t)(sizeof(proto));
        opt->opt_nflen += (u_int16_t)(sizeof((*pskb)->nh.ipv6h);
        if (fhdr) {
                u_int32_t hdrptr = (*pskb)->nh.ipv6h->nexthdr;
                int hdrlen = 0;
                while (*hdrptr != NEXTHDR_FRAGMENT) {
                        opt->opt_nflen += *hdrptr;
                        hdrlen = sizeof(*hdrptr);
                        hdrptr = (u_int32_t)(hdrptr + hdrlen);
                }
                hdrlen = sizeof(fhdr);
                hdrptr = (u_int32_t)(hdrptr + hdrlen);
                while (*hdrptr != proto) {
                        opt->opt_flen += hdrptr;
                        hdrlen = sizeof(*hdrptr);
                        hdrptr = (u_int32_t)(hdrptr + hdrlen);
                }
        }

        opt->tot_len = sizeof(opt);
        return;
}

static int ip6_refrag_getfrag(const void *data,
                              struct in6_addr *saddr,
                              char *buffer,
                              unsigned int offset,
                              unsigned int len)
{
        /* As far as I can tell, this function is designed to calculate various
        checksums for various portions of the header and payload of a packet.
        I may have to copy&paste or duplicate code */
        return 0;
}

unsigned int ip6_refrag(unsigned int hooknum,
                        struct sk_buff **pskb,
                        const struct net_device *in,
                        const struct net_device *out,
                        int (*okfn)(struct sk_buff *))
{
        struct rtable *rt = (struct rtable *)(*pskb)->dst;
        struct flowi *flow;
        struct ipv6_txoptions *opt;
        int ret, flags = 0;

        /* We've seen it coming out the other side: confirm it */
        if (ip6_conntrack_confirm(*pskb) != NF_ACCEPT)
                return NF_DROP;

        if ((*pskb)->len > rt->u.dst.pmtu) {           
                /* Let's get busy */
                init_fl(*pskb, flow);
                init_opt(*pskb, opt);

                /* Send the packet! */
                ret = ip6_build_xmit((*pskb)->sk,
                                     ip6_refrag_getfrag,
                                     get_ipv6_header((*pskb)->nh.ipv6h, 0),
                                     flow, (*pskb)->len, opt,
                                     (*pskb)->nh.ipv6h->hoplimit,
                                     flags);
                if (ret < 0) {
                        printk(KERN_WARNING "KABOOM! can't send fragged 
packet!\n");
                        return NF_DROP;
                }
                return NF_STOLEN;
        }
        return NF_ACCEPT;
}

/* Returns new sk_buff, or NULL */
struct sk_buff *
ip6_ct_gather_frags(struct sk_buff *skb)
{
        struct sock *sk = skb->sk;
        struct frag_hdr *fhdr =
           (struct frag_hdr *)get_ipv6_header(skb->nh.ipv6h, NEXTHDR_FRAGMENT);

#ifdef CONFIG_NETFILTER_DEBUG
        unsigned int olddebug = skb->nf_debug;
#endif

        if (sk) {
                sock_hold(sk);
                skb_orphan(skb);
        }

        /* Disable the local bh, reassemble the packet, and
        reenable the local bh */
        local_bh_disable();
        if (!ipv6_reassembly(skb, fhdr)) {
                if (sk) sock_put(sk);
                local_bh_enable();
                return NULL;
        }
        local_bh_enable();

        if (skb_is_nonlinear(skb) && skb_linearize(skb, GFP_ATOMIC) != 0) {
                kfree_skb(skb);
                if (sk) sock_put(sk);
                return NULL;
        }

        if (sk) {
                skb_set_owner_w(skb, sk);
                sock_put(sk);
        }

        skb->nfcache |= NFC_ALTERED;

#ifdef CONFIG_NETFILTER_DEBUG
        /* Packet path as if nothing had happened. */
        skb->nf_debug = olddebug;
#endif

        return skb;
}
<snip>

IPv6 header chain parsing
<snip>

/* Locate any available header in an IPv6 header */
u_int32_t
get_ipv6_header(const struct ipv6hdr *ipv6h, u_int8_t header)
{
        /* Get the bitmasked list of available headers */
        u_int32_t *hdrptr = (u_int32_t *)&ipv6h->nexthdr;
        int hdrlen, length;
        u_int32_t headers = 0;

        length = sizeof(ipv6h);
        hdrlen = 0;

        do {
                headers |= *hdrptr;
                hdrlen = sizeof((u_int32_t *)hdrptr);
                hdrptr = (u_int32_t *)(hdrptr + hdrlen);
                length -= hdrlen;
        }
        while (!(length < hdrlen));

        if (header == 0) {
                /* This is a special thing - gets a protocol
                for an unknown packet type - it uses the protocol_bitmask
                variable - initialized by default from the builtins, and
                (hopefully) later initialized by 3rd-party protocol modules */
                short int count = 0;
                for (count = 0; count < MAX_BUILTINS + 1; count++)
                        if (headers & protocol_bitmask[count])
                                return protocol_bitmask[count];
        }
                
        /* Is the one we want there? */
        if (headers & header)
                return header;

        /* NOTREACHED */
        return 0;
}
<snip>


<Prev in Thread] Current Thread [Next in Thread>