Commit f05cd468 authored by Mitsuru Kanda's avatar Mitsuru Kanda Committed by David S. Miller

[IPV6]: Process all extension headers via ipproto->handler.

parent 4228d368
......@@ -203,12 +203,8 @@ extern int ip6_ra_control(struct sock *sk, int sel,
extern int ip6_call_ra_chain(struct sk_buff *skb, int sel);
extern int ipv6_reassembly(struct sk_buff **skb, int);
extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
extern int ipv6_parse_exthdrs(struct sk_buff **skb, int);
extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
extern int ip6_frag_nqueues;
......
......@@ -44,7 +44,7 @@ struct inet_protocol
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
struct inet6_protocol
{
int (*handler)(struct sk_buff *skb);
int (*handler)(struct sk_buff **skbp);
void (*err_handler)(struct sk_buff *skb,
struct inet6_skb_parm *opt,
......
......@@ -15,6 +15,14 @@ extern struct proto tcpv6_prot;
struct flowi;
/* extention headers */
extern void ipv6_hopopts_init(void);
extern void ipv6_rthdr_init(void);
extern void ipv6_frag_init(void);
extern void ipv6_nodata_init(void);
extern void ipv6_destopt_init(void);
/* transport protocols */
extern void rawv6_init(void);
extern void udpv6_init(void);
extern void tcpv6_init(void);
......
......@@ -730,7 +730,7 @@ extern int xfrm_replay_check(struct xfrm_state *x, u32 seq);
extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
extern int xfrm6_rcv(struct sk_buff *skb);
extern int xfrm6_rcv(struct sk_buff **pskb);
extern int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir);
extern int xfrm_user_policy(struct sock *sk, int optname, u8 *optval, int optlen);
......
......@@ -799,6 +799,13 @@ static int __init inet6_init(void)
addrconf_init();
sit_init();
/* Init v6 extention headers. */
ipv6_hopopts_init();
ipv6_rthdr_init();
ipv6_frag_init();
ipv6_nodata_init();
ipv6_destopt_init();
/* Init v6 transport protocols. */
udpv6_init();
tcpv6_init();
......
......@@ -18,6 +18,9 @@
/* Changes:
* yoshfuji : ensure not to overrun while parsing
* tlv options.
* Mitsuru KANDA @USAGI : Remove ipv6_parse_exthdrs().
* : Register inbound extention header
* : handlers as inet6_protocol{}.
*/
#include <linux/errno.h>
......@@ -43,20 +46,6 @@
#include <asm/uaccess.h>
/*
* Parsing inbound headers.
*
* Parsing function "func" returns offset wrt skb->nh of the place,
* where next nexthdr value is stored or NULL, if parsing
* failed. It should also update skb->h tp point at the next header.
*/
struct hdrtype_proc
{
int type;
int (*func) (struct sk_buff **, int offset);
};
/*
* Parsing tlv encoded headers.
*
......@@ -164,49 +153,77 @@ static struct tlvtype_proc tlvprocdestopt_lst[] = {
{-1, NULL}
};
static int ipv6_dest_opt(struct sk_buff **skb_ptr, int nhoff)
static int ipv6_destopt_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb=*skb_ptr;
struct sk_buff *skb = *skbp;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
u8 nexthdr = 0;
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
kfree_skb(skb);
return -1;
return 0;
}
nexthdr = ((struct ipv6_destopt_hdr *)skb->h.raw)->nexthdr;
opt->dst1 = skb->h.raw - skb->nh.raw;
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
skb->h.raw += ((skb->h.raw[1]+1)<<3);
return opt->dst1;
return -nexthdr;
}
return 0;
}
return -1;
static struct inet6_protocol destopt_protocol =
{
.handler = ipv6_destopt_rcv,
};
void __init ipv6_destopt_init(void)
{
if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0)
printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");
}
/********************************
NONE header. No data in packet.
********************************/
static int ipv6_nodata(struct sk_buff **skb_ptr, int nhoff)
static int ipv6_nodata_rcv(struct sk_buff **skbp)
{
kfree_skb(*skb_ptr);
return -1;
struct sk_buff *skb = *skbp;
kfree_skb(skb);
return 0;
}
static struct inet6_protocol nodata_protocol =
{
.handler = ipv6_nodata_rcv,
};
void __init ipv6_nodata_init(void)
{
if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0)
printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");
}
/********************************
Routing header.
********************************/
static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
static int ipv6_rthdr_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb = *skb_ptr;
struct sk_buff *skb = *skbp;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
struct in6_addr *addr;
struct in6_addr daddr;
int addr_type;
int n, i;
u8 nexthdr = 0;
struct ipv6_rt_hdr *hdr;
struct rt0_hdr *rthdr;
......@@ -215,15 +232,16 @@ static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
IP6_INC_STATS_BH(Ip6InHdrErrors);
kfree_skb(skb);
return -1;
return 0;
}
hdr = (struct ipv6_rt_hdr *) skb->h.raw;
nexthdr = hdr->nexthdr;
if ((ipv6_addr_type(&skb->nh.ipv6h->daddr)&IPV6_ADDR_MULTICAST) ||
skb->pkt_type != PACKET_HOST) {
kfree_skb(skb);
return -1;
return 0;
}
looped_back:
......@@ -232,24 +250,24 @@ static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
skb->h.raw += (hdr->hdrlen + 1) << 3;
opt->dst0 = opt->dst1;
opt->dst1 = 0;
return (&hdr->nexthdr) - skb->nh.raw;
return -nexthdr;
}
if (hdr->type != IPV6_SRCRT_TYPE_0 || (hdr->hdrlen & 0x01)) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, hdr->type != IPV6_SRCRT_TYPE_0 ? 2 : 1);
return -1;
return 0;
}
/*
* This is the routing header forwarding algorithm from
* RFC 1883, page 17.
* RFC 2460, page 16.
*/
n = hdr->hdrlen >> 1;
if (hdr->segments_left > n) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
return -1;
return 0;
}
/* We are about to mangle packet header. Be careful!
......@@ -259,8 +277,8 @@ static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
kfree_skb(skb);
if (skb2 == NULL)
return -1;
*skb_ptr = skb = skb2;
return 0;
*skbp = skb = skb2;
opt = (struct inet6_skb_parm *)skb2->cb;
hdr = (struct ipv6_rt_hdr *) skb2->h.raw;
}
......@@ -278,7 +296,7 @@ static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
if (addr_type&IPV6_ADDR_MULTICAST) {
kfree_skb(skb);
return -1;
return 0;
}
ipv6_addr_copy(&daddr, addr);
......@@ -289,23 +307,34 @@ static int ipv6_routing_header(struct sk_buff **skb_ptr, int nhoff)
ip6_route_input(skb);
if (skb->dst->error) {
dst_input(skb);
return -1;
return 0;
}
if (skb->dst->dev->flags&IFF_LOOPBACK) {
if (skb->nh.ipv6h->hop_limit <= 1) {
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
0, skb->dev);
kfree_skb(skb);
return -1;
return 0;
}
skb->nh.ipv6h->hop_limit--;
goto looped_back;
}
dst_input(skb);
return -1;
return 0;
}
static struct inet6_protocol rthdr_protocol =
{
.handler = ipv6_rthdr_rcv,
};
void __init ipv6_rthdr_init(void)
{
if (inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING) < 0)
printk(KERN_ERR "ipv6_rthdr_init: Could not register protocol\n");
};
/*
This function inverts received rthdr.
NOTE: specs allow to make it automatically only if
......@@ -371,97 +400,6 @@ ipv6_invert_rthdr(struct sock *sk, struct ipv6_rt_hdr *hdr)
return opt;
}
/********************************
AUTH header.
********************************/
/*
rfc1826 said, that if a host does not implement AUTH header
it MAY ignore it. We use this hole 8)
Actually, now we can implement OSPFv6 without kernel IPsec.
Authentication for poors may be done in user space with the same success.
Yes, it means, that we allow application to send/receive
raw authentication header. Apparently, we suppose, that it knows
what it does and calculates authentication data correctly.
Certainly, it is possible only for udp and raw sockets, but not for tcp.
AUTH header has 4byte granular length, which kills all the idea
behind AUTOMATIC 64bit alignment of IPv6. Now we will lose
cpu ticks, checking that sender did not something stupid
and opt->hdrlen is even. Shit! --ANK (980730)
*/
static int ipv6_auth_hdr(struct sk_buff **skb_ptr, int nhoff)
{
struct sk_buff *skb=*skb_ptr;
struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
int len;
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
goto fail;
/*
* RFC2402 2.2 Payload Length
* The 8-bit field specifies the length of AH in 32-bit words
* (4-byte units), minus "2".
* -- Noriaki Takamiya @USAGI Project
*/
len = (skb->h.raw[1]+2)<<2;
if (len&7)
goto fail;
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len))
goto fail;
opt->auth = skb->h.raw - skb->nh.raw;
skb->h.raw += len;
return opt->auth;
fail:
kfree_skb(skb);
return -1;
}
/* This list MUST NOT contain entry for NEXTHDR_HOP.
It is parsed immediately after packet received
and if it occurs somewhere in another place we must
generate error.
*/
static struct hdrtype_proc hdrproc_lst[] = {
{NEXTHDR_FRAGMENT, ipv6_reassembly},
{NEXTHDR_ROUTING, ipv6_routing_header},
{NEXTHDR_DEST, ipv6_dest_opt},
{NEXTHDR_NONE, ipv6_nodata},
{NEXTHDR_AUTH, ipv6_auth_hdr},
/*
{NEXTHDR_ESP, ipv6_esp_hdr},
*/
{-1, NULL}
};
int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff)
{
struct hdrtype_proc *hdrt;
u8 nexthdr = (*skb_in)->nh.raw[nhoff];
restart:
for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
if (hdrt->type == nexthdr) {
if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) {
nexthdr = (*skb_in)->nh.raw[nhoff];
goto restart;
}
return -1;
}
}
return nhoff;
}
/**********************************
Hop-by-hop options.
**********************************/
......@@ -532,6 +470,34 @@ int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
return -1;
}
/* This is fake. We have already parsed hopopts in ipv6_rcv(). -mk */
static int ipv6_hopopts_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
u8 nexthdr = 0;
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
kfree_skb(skb);
return 0;
}
nexthdr = ((struct ipv6_hopopt_hdr *)skb->h.raw)->nexthdr;
skb->h.raw += (skb->h.raw[1]+1)<<3;
return -nexthdr;
}
static struct inet6_protocol hopopts_protocol =
{
.handler = ipv6_hopopts_rcv,
};
void __init ipv6_hopopts_init(void)
{
if (inet6_add_protocol(&hopopts_protocol, IPPROTO_HOPOPTS) < 0)
printk(KERN_ERR "ipv6_hopopts_init: Could not register protocol\n");
}
/*
* Creating outbound headers.
*
......
......@@ -74,7 +74,7 @@ DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
static struct socket *__icmpv6_socket[NR_CPUS];
#define icmpv6_socket __icmpv6_socket[smp_processor_id()]
static int icmpv6_rcv(struct sk_buff *skb);
static int icmpv6_rcv(struct sk_buff **pskb);
static struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
......@@ -459,8 +459,9 @@ static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info)
* Handle icmp messages
*/
static int icmpv6_rcv(struct sk_buff *skb)
static int icmpv6_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct net_device *dev = skb->dev;
struct in6_addr *saddr, *daddr;
struct ipv6hdr *orig_hdr;
......
......@@ -15,6 +15,10 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/* Changes
*
* Mitsuru KANDA @USAGI : Remove ipv6_parse_exthdrs().
*/
#include <linux/errno.h>
#include <linux/types.h>
......@@ -126,39 +130,11 @@ static inline int ip6_input_finish(struct sk_buff *skb)
struct ipv6hdr *hdr = skb->nh.ipv6h;
struct inet6_protocol *ipprot;
struct sock *raw_sk;
int nhoff;
int nexthdr;
int nexthdr = hdr->nexthdr;
u8 hash;
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
/*
* Parse extension headers
*/
nexthdr = hdr->nexthdr;
nhoff = offsetof(struct ipv6hdr, nexthdr);
/* Skip hop-by-hop options, they are already parsed. */
if (nexthdr == NEXTHDR_HOP) {
nhoff = sizeof(struct ipv6hdr);
nexthdr = skb->h.raw[0];
skb->h.raw += (skb->h.raw[1]+1)<<3;
}
/* This check is sort of optimization.
It would be stupid to detect for optional headers,
which are missing with probability of 200%
*/
if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP &&
nexthdr != NEXTHDR_AUTH && nexthdr != NEXTHDR_ESP) {
nhoff = ipv6_parse_exthdrs(&skb, nhoff);
if (nhoff < 0)
return 0;
nexthdr = skb->nh.raw[nhoff];
hdr = skb->nh.ipv6h;
}
if (!pskb_pull(skb, skb->h.raw - skb->data))
goto discard;
......@@ -173,7 +149,7 @@ static inline int ip6_input_finish(struct sk_buff *skb)
hash = nexthdr & (MAX_INET_PROTOS - 1);
if ((ipprot = inet6_protos[hash]) != NULL) {
int ret = ipprot->handler(skb);
int ret = ipprot->handler(&skb);
if (ret < 0) {
nexthdr = -ret;
goto resubmit;
......@@ -182,7 +158,8 @@ static inline int ip6_input_finish(struct sk_buff *skb)
} else {
if (!raw_sk) {
IP6_INC_STATS_BH(Ip6InUnknownProtos);
icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR,
offsetof(struct ipv6hdr, nexthdr));
} else {
IP6_INC_STATS_BH(Ip6InDelivers);
kfree_skb(skb);
......
......@@ -23,6 +23,7 @@
* Horst von Brand Add missing #include <linux/string.h>
* Alexey Kuznetsov SMP races, threading, cleanup.
* Patrick McHardy LRU queue of frag heads for evictor.
* Mitsuru KANDA @USAGI Register inet6_protocol{}.
*/
#include <linux/config.h>
#include <linux/errno.h>
......@@ -525,6 +526,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
int remove_fraghdr = 0;
int payload_len;
int nhoff;
u8 nexthdr = 0;
fq_kill(fq);
......@@ -535,6 +537,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len;
nhoff = head->h.raw - head->nh.raw;
nexthdr = ((struct frag_hdr*)head->h.raw)->nexthdr;
if (payload_len > 65535) {
payload_len -= 8;
if (payload_len > 65535)
......@@ -609,9 +613,12 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
if (head->ip_summed == CHECKSUM_HW)
head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
if (!pskb_pull(head, head->h.raw - head->data))
goto out_fail;
IP6_INC_STATS_BH(Ip6ReasmOKs);
fq->fragments = NULL;
return nhoff;
return nexthdr;
out_oversize:
if (net_ratelimit())
......@@ -622,16 +629,18 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
out_fail:
IP6_INC_STATS_BH(Ip6ReasmFails);
return -1;
return 0;
}
int ipv6_reassembly(struct sk_buff **skbp, int nhoff)
static int ipv6_frag_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
struct net_device *dev = skb->dev;
struct frag_hdr *fhdr;
struct frag_queue *fq;
struct ipv6hdr *hdr;
int nhoff = skb->h.raw - skb->nh.raw;
u8 nexthdr = 0;
hdr = skb->nh.ipv6h;
......@@ -640,15 +649,16 @@ int ipv6_reassembly(struct sk_buff **skbp, int nhoff)
/* Jumbo payload inhibits frag. header */
if (hdr->payload_len==0) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
return -1;
goto discard;
}
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
return -1;
goto discard;
}
hdr = skb->nh.ipv6h;
fhdr = (struct frag_hdr *)skb->h.raw;
nexthdr = fhdr->nexthdr;
if (!(fhdr->frag_off & htons(0xFFF9))) {
/* It is not a fragmented frame */
......@@ -674,10 +684,22 @@ int ipv6_reassembly(struct sk_buff **skbp, int nhoff)
spin_unlock(&fq->lock);
fq_put(fq);
return ret;
return -ret;
}
discard:
IP6_INC_STATS_BH(Ip6ReasmFails);
kfree_skb(skb);
return -1;
return 0;
}
static struct inet6_protocol frag_protocol =
{
.handler = ipv6_frag_rcv,
};
void __init ipv6_frag_init(void)
{
if (inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT) < 0)
printk(KERN_ERR "ipv6_frag_init: Could not register protocol\n");
}
......@@ -1591,8 +1591,9 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
return 0;
}
static int tcp_v6_rcv(struct sk_buff *skb)
static int tcp_v6_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct tcphdr *th;
struct sock *sk;
int ret;
......
......@@ -641,8 +641,9 @@ static void udpv6_mcast_deliver(struct udphdr *uh,
read_unlock(&udp_hash_lock);
}
static int udpv6_rcv(struct sk_buff *skb)
static int udpv6_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct sock *sk;
struct udphdr *uh;
struct net_device *dev = skb->dev;
......
......@@ -123,8 +123,9 @@ int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir)
return nexthdr;
}
int xfrm6_rcv(struct sk_buff *skb)
int xfrm6_rcv(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
int err;
u32 spi, seq;
struct xfrm_state *xfrm_vec[XFRM_MAX_DEPTH];
......@@ -137,12 +138,8 @@ int xfrm6_rcv(struct sk_buff *skb)
u16 nh_offset = 0;
u8 nexthdr = 0;
if (hdr->nexthdr == IPPROTO_AH || hdr->nexthdr == IPPROTO_ESP) {
nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
hdr_len = sizeof(struct ipv6hdr);
} else {
hdr_len = skb->h.raw - skb->nh.raw;
}
nh_offset = ((unsigned char*)&skb->nh.ipv6h->nexthdr) - skb->nh.raw;
hdr_len = sizeof(struct ipv6hdr);
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr)
......@@ -189,20 +186,6 @@ int xfrm6_rcv(struct sk_buff *skb)
xfrm_vec[xfrm_nr++] = x;
iph = skb->nh.ipv6h; /* ??? */
if (nexthdr == NEXTHDR_DEST) {
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
err = -EINVAL;
goto drop;
}
nexthdr = skb->h.raw[0];
nh_offset = skb->h.raw - skb->nh.raw;
skb_pull(skb, (skb->h.raw[1]+1)<<3);
skb->h.raw = skb->data;
}
if (x->props.mode) { /* XXX */
if (iph->nexthdr != IPPROTO_IPV6)
goto drop;
......
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