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>
|