From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kunihiro Ishiguro Subject: Re: [PATCH] IPv6 IPsec support Date: Wed, 19 Feb 2003 15:10:53 -0800 Sender: netdev-bounce@oss.sgi.com Message-ID: <87bs17hnnm.wl@ipinfusion.com> References: <20030219134850.5f203ea7.Kazunori.Miyazawa@jp.yokogawa.com> <20030218.233301.98333082.davem@redhat.com> Mime-Version: 1.0 (generated by SEMI 1.14.3 - "Ushinoya") Content-Type: text/plain; charset=US-ASCII Cc: "David S. Miller" , kuznet@ms2.inr.ac.ru, netdev@oss.sgi.com, usagi-core@linux-ipv6.org Return-path: To: Mitsuru KANDA / =?ISO-2022-JP?B?GyRCP0BFRBsoQiAbJEI9PBsoQg==?= In-Reply-To: Errors-to: netdev-bounce@oss.sgi.com List-Id: netdev.vger.kernel.org This will not be useful other than Miyazaki/Kanda. I've applied miyazaki's patch then try to diff against local code. o xfrm6_selector_match() fix o No need of option field mutation in xfrm6_rcv(). It is moved to ah.c. o Setting Routing Header's segment_lefts to 0 is wrong. Let's let it be. o xfrm6_rcv() try to figure out he is processing AH or ESP by ipv6hdr->protocol... But when other extenstion header exits this could be wrong. Initial protocol value is passed from the caller. o Some cosmetic change. And this patch include o Not removing AH header o Mutation field provisioning But there changes are no needed. Miyazaki, would you mind to take a look into this? Have fun ;-). -- Kunihiro Ishiguro diff -ruN linux-2.5.62.orig/include/net/ipv6.h linux-2.5.62/include/net/ipv6.h --- linux-2.5.62.orig/include/net/ipv6.h 2003-02-14 15:52:28.000000000 -0800 +++ linux-2.5.62/include/net/ipv6.h 2003-02-19 13:19:38.000000000 -0800 @@ -41,7 +41,7 @@ #define NEXTHDR_MAX 255 - +#define IP6OPT_MUTABLE 0x20 #define IPV6_DEFAULT_HOPLIMIT 64 #define IPV6_DEFAULT_MCASTHOPS 1 diff -ruN linux-2.5.62.orig/include/net/xfrm.h linux-2.5.62/include/net/xfrm.h --- linux-2.5.62.orig/include/net/xfrm.h 2003-02-19 14:24:53.000000000 -0800 +++ linux-2.5.62/include/net/xfrm.h 2003-02-19 14:03:20.000000000 -0800 @@ -414,7 +414,7 @@ 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 *skb, u8 proto); 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); @@ -452,11 +452,37 @@ extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name); extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name); +static __inline__ int addr_match(void *token1, void *token2, int prefixlen) +{ + __u32 *a1 = token1; + __u32 *a2 = token2; + int pdw; + int pbi; + + pdw = prefixlen >> 5; /* num of whole __u32 in prefix */ + pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ + + if (pdw) + if (memcmp(a1, a2, pdw << 2)) + return 0; + + if (pbi) { + __u32 mask; + + mask = htonl((0xffffffff) << (32 - pbi)); + + if ((a1[pdw] ^ a2[pdw]) & mask) + return 0; + } + + return 1; +} + static inline int xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl) { - return !memcmp(fl->fl6_dst, &sel->daddr, (sel->prefixlen_d)/8) && - !memcmp(fl->fl6_src, &sel->saddr, (sel->prefixlen_s)/8) && + return !addr_match(fl->fl6_dst, &sel->daddr, sel->prefixlen_d) && + !addr_match(fl->fl6_src, &sel->saddr, sel->prefixlen_s) && !((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) && !((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) && (fl->proto == sel->proto || !sel->proto) && diff -ruN -x '*.o' -x '*.cmd' -x '*.ko' -x '*.mod.c' linux-2.5.62.orig/net/ipv6/ah.c linux-2.5.62/net/ipv6/ah.c --- linux-2.5.62.orig/net/ipv6/ah.c 2003-02-19 14:24:53.000000000 -0800 +++ linux-2.5.62/net/ipv6/ah.c 2003-02-19 14:31:12.000000000 -0800 @@ -32,7 +32,6 @@ #define AH_HLEN_NOICV 12 -/* XXX no ipv6 ah specific */ #define NIP6(addr) \ ntohs((addr).s6_addr16[0]),\ ntohs((addr).s6_addr16[1]),\ @@ -43,6 +42,214 @@ ntohs((addr).s6_addr16[6]),\ ntohs((addr).s6_addr16[7]) +static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) +{ + u8 *opt = (u8 *)opthdr; + int len = ipv6_optlen(opthdr); + int off = 0; + int optlen = 0; + + off += 2; + len -= 2; + + while (len > 0) { + + switch (opt[off]) { + + case IPV6_TLV_PAD0: + optlen = 1; + break; + default: + if (len < 2) + goto bad; + optlen = opt[off+1]+2; + if (len < optlen) + goto bad; + if (opt[off] & 0x20) + memset(&opt[off+2], 0, opt[off+1]); + break; + } + + off += optlen; + len -= optlen; + } + if (len == 0) + return 1; + +bad: + return 0; +} + +int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir) +{ + u16 offset = sizeof(struct ipv6hdr); + struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + unsigned int packet_len = skb->tail - skb->nh.raw; + u8 nexthdr = skb->nh.ipv6h->nexthdr; + u8 nextnexthdr = 0; + + *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; + + while (offset + 1 <= packet_len) { + + switch (nexthdr) { + + case NEXTHDR_HOP: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun hopopts\n"); + return 0; + } + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_ROUTING: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_DEST: + *nh_offset = offset; + offset += ipv6_optlen(exthdr); + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun destopt\n"); + return 0; + } + nexthdr = exthdr->nexthdr; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + break; + + case NEXTHDR_AUTH: + if (dir == XFRM_POLICY_OUT) { + memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0, + (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2); + } + if (exthdr->nexthdr == NEXTHDR_DEST) { + offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2; + exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); + nextnexthdr = exthdr->nexthdr; + if (!zero_out_mutable_opts(exthdr)) { + if (net_ratelimit()) + printk(KERN_WARNING "overrun destopt\n"); + return 0; + } + } + return nexthdr; + default: + return nexthdr; + } + } + + return nexthdr; +} + +static int ipv6_check_mutable_options(struct sk_buff *skb, struct ipv6hdr *hdr, + struct inet6_skb_parm *opt) +{ + int lim; + u8 *optpnt; + u8 nexthdr = hdr->nexthdr; + int datalen = 0; + + optpnt = (u8*)(hdr+1); + lim = ntohs(hdr->payload_len); + + while (lim > 0) { + struct ipv6_opt_hdr *opthdr; + int hdrlen; + + opthdr = (struct ipv6_opt_hdr*)optpnt; + + switch(nexthdr) { + case NEXTHDR_HOP: + opt->hop = optpnt - skb->nh.raw; + hdrlen = ipv6_optlen(opthdr); + datalen += (hdrlen - 2); + break; + case NEXTHDR_DEST: + opt->dst1 = optpnt - skb->nh.raw; + hdrlen = ipv6_optlen(opthdr); + datalen += (hdrlen - 2); + break; + case NEXTHDR_ROUTING: + case NEXTHDR_FRAGMENT: + case NEXTHDR_NONE: + hdrlen = ipv6_optlen(opthdr); + break; + case NEXTHDR_AUTH: + hdrlen = (opthdr->hdrlen + 2) << 2; + break; + default: + goto out; + } + nexthdr = opthdr->nexthdr; + optpnt += hdrlen; + lim -= hdrlen; + } +out: + return datalen; +} + +static int ah6_set_option(u8 *opthdr, u8 **opt_data, int erase) +{ + u8 *optpnt = opthdr; + int len = ipv6_optlen((struct ipv6_opt_hdr*)opthdr); + int datalen; + int optlen; + + optpnt += 2; + len -= 2; + datalen = len; + + if (erase) { + memcpy(*opt_data, optpnt, datalen); + + while (len > 0) { + if (optpnt[0] == IPV6_TLV_PAD0) { + optlen = 1; + } else { + if (len < 2) + return -1; + optlen = optpnt[1] + 2; + if (optlen > len) + return -1; + if (optpnt[0] & IP6OPT_MUTABLE) + memset(optpnt+2, 0, optpnt[1]); + } + optpnt += optlen; + len -= optlen; + } + } else { + memcpy(optpnt, *opt_data, datalen); + } + + *opt_data += datalen; + + return 0; +} + +static inline void ah6_clear_mutable_options(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 *opt_data) +{ + if (opt->hop) + ah6_set_option(skb->nh.raw+opt->hop, &opt_data, 1); + if (opt->dst1) + ah6_set_option(skb->nh.raw+opt->dst1, &opt_data, 1); +} + +static inline void ah6_restore_mutable_options(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 *opt_data) +{ + if (opt->hop) + ah6_set_option(skb->nh.raw+opt->hop, &opt_data, 0); + if (opt->dst1) + ah6_set_option(skb->nh.raw+opt->dst1, &opt_data, 0); +} + int ah6_output(struct sk_buff *skb) { int err; @@ -50,6 +257,7 @@ struct dst_entry *dst = skb->dst; struct xfrm_state *x = dst->xfrm; struct ipv6hdr *iph = NULL; + struct ipv6hdr *top_hdr; struct ip_auth_hdr *ah; struct ah_data *ahp; u16 nh_offset = 0; @@ -66,13 +274,13 @@ if (x->props.mode) { iph = skb->nh.ipv6h; - skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len); - skb->nh.ipv6h->version = 6; - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); - skb->nh.ipv6h->nexthdr = IPPROTO_AH; - memcpy(&skb->nh.ipv6h->saddr, &x->props.saddr, sizeof(struct in6_addr)); - memcpy(&skb->nh.ipv6h->daddr, &x->id.daddr, sizeof(struct in6_addr)); - ah = (struct ip_auth_hdr*)(skb->nh.ipv6h+1); + top_hdr = (struct ipv6hdr*)skb_push(skb, x->props.header_len); + top_hdr->version = 6; + top_hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + top_hdr->nexthdr = IPPROTO_AH; + memcpy(&top_hdr->saddr, &x->props.saddr, sizeof(struct in6_addr)); + memcpy(&top_hdr->daddr, &x->id.daddr, sizeof(struct in6_addr)); + ah = (struct ip_auth_hdr*)(top_hdr+1); ah->nexthdr = IPPROTO_IPV6; } else { hdr_len = skb->h.raw - skb->nh.raw; @@ -82,42 +290,40 @@ goto error; } memcpy(iph, skb->data, hdr_len); - skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, x->props.header_len); - memcpy(skb->nh.ipv6h, iph, hdr_len); + top_hdr = (struct ipv6hdr*)skb_push(skb, x->props.header_len); + memcpy(top_hdr, iph, hdr_len); nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_OUT); if (nexthdr == 0) goto error; skb->nh.raw[nh_offset] = IPPROTO_AH; - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + top_hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); ah = (struct ip_auth_hdr*)(skb->nh.raw+hdr_len); ah->nexthdr = nexthdr; } - skb->nh.ipv6h->priority = 0; - skb->nh.ipv6h->flow_lbl[0] = 0; - skb->nh.ipv6h->flow_lbl[1] = 0; - skb->nh.ipv6h->flow_lbl[2] = 0; - skb->nh.ipv6h->hop_limit = 0; + skb->nh.ipv6h = top_hdr; + top_hdr->priority = 0; + memset(top_hdr->flow_lbl, 0, 3); + top_hdr->hop_limit = 0; ahp = x->data; ah->hdrlen = (XFRM_ALIGN8(ahp->icv_trunc_len + - AH_HLEN_NOICV) >> 2) - 2; + AH_HLEN_NOICV) >> 2) - 2; + ah->reserved = 0; ah->spi = x->id.spi; ah->seq_no = htonl(++x->replay.oseq); ahp->icv(ahp, skb, ah->auth_data); if (x->props.mode) { - skb->nh.ipv6h->hop_limit = iph->hop_limit; - skb->nh.ipv6h->priority = iph->priority; - skb->nh.ipv6h->flow_lbl[0] = iph->flow_lbl[0]; - skb->nh.ipv6h->flow_lbl[1] = iph->flow_lbl[1]; - skb->nh.ipv6h->flow_lbl[2] = iph->flow_lbl[2]; + top_hdr->priority = iph->priority; + memcpy(top_hdr->flow_lbl, iph->flow_lbl, 3); + top_hdr->hop_limit = iph->hop_limit; } else { - memcpy(skb->nh.ipv6h, iph, hdr_len); + memcpy(top_hdr, iph, hdr_len); skb->nh.raw[nh_offset] = IPPROTO_AH; - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + top_hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); kfree (iph); } @@ -139,42 +345,48 @@ int ah6_input(struct xfrm_state *x, struct sk_buff *skb) { int ah_hlen; - struct ipv6hdr *iph; + struct ipv6hdr *hdr; struct ipv6_auth_hdr *ah; struct ah_data *ahp; - unsigned char *tmp_hdr = NULL; - int hdr_len = skb->h.raw - skb->nh.raw; - u8 nexthdr = 0; + struct inet6_skb_parm opt; + char work_buf[8]; + int optlen; + u8 *opt_data = NULL; if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr))) goto out; ah = (struct ipv6_auth_hdr*)skb->data; - ahp = x->data; - ah_hlen = (ah->hdrlen + 2) << 2; + ah_hlen = (ah->hdrlen + 2) << 2; - if (ah_hlen != XFRM_ALIGN8(ahp->icv_full_len + AH_HLEN_NOICV) && - ah_hlen != XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV)) - goto out; + if (ah_hlen != XFRM_ALIGN8(ahp->icv_full_len + AH_HLEN_NOICV) && + ah_hlen != XFRM_ALIGN8(ahp->icv_trunc_len + AH_HLEN_NOICV)) + goto out; if (!pskb_may_pull(skb, (ah->hdrlen+2)<<2)) goto out; - /* We are going to _remove_ AH header to keep sockets happy, - * so... Later this can change. */ - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - goto out; - tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); - if (!tmp_hdr) - goto out; - memcpy(tmp_hdr, skb->nh.raw, hdr_len); - ah = (struct ipv6_auth_hdr*)skb->data; - iph = skb->nh.ipv6h; + hdr = skb->nh.ipv6h; + memcpy(work_buf, hdr, 8); + hdr->priority = 0; + memset(hdr->flow_lbl, 0, 3); + hdr->hop_limit = 0; + + memset(&opt, 0, sizeof(struct inet6_skb_parm)); + optlen = ipv6_check_mutable_options(skb, hdr, &opt); + if (optlen < 0) + goto out; { u8 auth_data[ahp->icv_trunc_len]; + + if (optlen) { + opt_data = kmalloc(optlen, GFP_ATOMIC); + if (!opt_data) + goto out; + ah6_clear_mutable_options(skb, &opt, opt_data); + } memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); memset(ah->auth_data, 0, ahp->icv_trunc_len); skb_push(skb, skb->data - skb->nh.raw); @@ -185,22 +397,19 @@ x->stats.integrity_failed++; goto free_out; } + if (optlen) { + ah6_restore_mutable_options(skb, &opt, opt_data); + kfree (opt_data); + } } + memcpy(hdr, work_buf, 8); + skb->h.raw += (ah->hdrlen+2)<<2; + skb->data = skb->h.raw; - nexthdr = ah->nexthdr; - skb->nh.raw = skb_pull(skb, (ah->hdrlen+2)<<2); - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); - skb_pull(skb, hdr_len); - skb->h.raw = skb->data; - - - kfree(tmp_hdr); - - return nexthdr; - + return ah->nexthdr; free_out: - kfree(tmp_hdr); + if (optlen) + kfree(opt_data); out: return -EINVAL; } @@ -208,22 +417,19 @@ void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, int type, int code, int offset, __u32 info) { - struct ipv6hdr *iph = (struct ipv6hdr*)skb->data; + struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data; struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset); struct xfrm_state *x; - if (type != ICMPV6_DEST_UNREACH || - type != ICMPV6_PKT_TOOBIG) + if (type != ICMPV6_DEST_UNREACH || type != ICMPV6_PKT_TOOBIG) return; - x = xfrm6_state_lookup(&iph->daddr, ah->spi, IPPROTO_AH); + x = xfrm6_state_lookup(&hdr->daddr, ah->spi, IPPROTO_AH); if (!x) return; - printk(KERN_DEBUG "pmtu discvovery on SA AH/%08x/" "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", - ntohl(ah->spi), NIP6(iph->daddr)); - + ntohl(ah->spi), NIP6(hdr->daddr)); xfrm_state_put(x); } @@ -315,26 +521,29 @@ .output = ah6_output }; +static inline int +xfrm6_ah_rcv(struct sk_buff *skb) +{ + return xfrm6_rcv(skb, IPPROTO_AH); +} + static struct inet6_protocol ah6_protocol = { - .handler = xfrm6_rcv, + .handler = xfrm6_ah_rcv, .err_handler = ah6_err, }; int __init ah6_init(void) { SET_MODULE_OWNER(&ah6_type); - if (xfrm6_register_type(&ah6_type) < 0) { printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n"); return -EAGAIN; } - if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { printk(KERN_INFO "ipv6 ah init: can't add protocol\n"); xfrm6_unregister_type(&ah6_type); return -EAGAIN; } - return 0; } @@ -342,10 +551,8 @@ { if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) printk(KERN_INFO "ipv6 ah close: can't remove protocol\n"); - if (xfrm6_unregister_type(&ah6_type) < 0) printk(KERN_INFO "ipv6 ah close: can't remove xfrm type\n"); - } module_init(ah6_init); diff -ruN -x '*.o' -x '*.cmd' -x '*.ko' -x '*.mod.c' linux-2.5.62.orig/net/ipv6/esp.c linux-2.5.62/net/ipv6/esp.c --- linux-2.5.62.orig/net/ipv6/esp.c 2003-02-19 14:24:53.000000000 -0800 +++ linux-2.5.62/net/ipv6/esp.c 2003-02-19 14:20:43.000000000 -0800 @@ -35,10 +35,6 @@ #include #define MAX_SG_ONSTACK 4 -#if 0 -typedef void (icv_update_fn_t)(struct crypto_tfm *, - struct scatterlist *, unsigned int); -#endif /* XXX no ipv6 esp specific */ #define NIP6(addr) \ @@ -545,8 +541,14 @@ .output = esp6_output }; +static inline int +xfrm6_esp_rcv(struct sk_buff *skb) +{ + return xfrm6_rcv(skb, IPPROTO_ESP); +} + static struct inet6_protocol esp6_protocol = { - .handler = xfrm6_rcv, + .handler = xfrm6_esp_rcv, .err_handler = esp6_err, }; diff -ruN -x '*.o' -x '*.cmd' -x '*.ko' -x '*.mod.c' linux-2.5.62.orig/net/ipv6/xfrm_input.c linux-2.5.62/net/ipv6/xfrm_input.c --- linux-2.5.62.orig/net/ipv6/xfrm_input.c 2003-02-19 14:24:53.000000000 -0800 +++ linux-2.5.62/net/ipv6/xfrm_input.c 2003-02-19 14:06:49.000000000 -0800 @@ -30,11 +30,11 @@ /* Fetch spi and seq frpm ipsec header */ -static int xfrm6_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) +static int xfrm6_parse_spi(struct sk_buff *skb, u8 proto, u32 *spi, u32 *seq) { int offset, offset_seq; - switch (nexthdr) { + switch (proto) { case IPPROTO_AH: offset = offsetof(struct ip_auth_hdr, spi); offset_seq = offsetof(struct ip_auth_hdr, seq_no); @@ -61,115 +61,7 @@ return 0; } -static int zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr) -{ - u8 *opt = (u8 *)opthdr; - int len = ipv6_optlen(opthdr); - int off = 0; - int optlen = 0; - - off += 2; - len -= 2; - - while (len > 0) { - - switch (opt[off]) { - - case IPV6_TLV_PAD0: - optlen = 1; - break; - default: - if (len < 2) - goto bad; - optlen = opt[off+1]+2; - if (len < optlen) - goto bad; - if (opt[off] & 0x20) - memset(&opt[off+2], 0, opt[off+1]); - break; - } - - off += optlen; - len -= optlen; - } - if (len == 0) - return 1; - -bad: - return 0; -} - -int xfrm6_clear_mutable_options(struct sk_buff *skb, u16 *nh_offset, int dir) -{ - u16 offset = sizeof(struct ipv6hdr); - struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - unsigned int packet_len = skb->tail - skb->nh.raw; - u8 nexthdr = skb->nh.ipv6h->nexthdr; - u8 nextnexthdr = 0; - - *nh_offset = ((unsigned char *)&skb->nh.ipv6h->nexthdr) - skb->nh.raw; - - while (offset + 1 <= packet_len) { - - switch (nexthdr) { - - case NEXTHDR_HOP: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun hopopts\n"); - return 0; - } - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_ROUTING: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - ((struct ipv6_rt_hdr*)exthdr)->segments_left = 0; - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_DEST: - *nh_offset = offset; - offset += ipv6_optlen(exthdr); - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun destopt\n"); - return 0; - } - nexthdr = exthdr->nexthdr; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - break; - - case NEXTHDR_AUTH: - if (dir == XFRM_POLICY_OUT) { - memset(((struct ipv6_auth_hdr*)exthdr)->auth_data, 0, - (((struct ipv6_auth_hdr*)exthdr)->hdrlen - 1) << 2); - } - if (exthdr->nexthdr == NEXTHDR_DEST) { - offset += (((struct ipv6_auth_hdr*)exthdr)->hdrlen + 2) << 2; - exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset); - nextnexthdr = exthdr->nexthdr; - if (!zero_out_mutable_opts(exthdr)) { - if (net_ratelimit()) - printk(KERN_WARNING "overrun destopt\n"); - return 0; - } - } - return nexthdr; - default: - return nexthdr; - } - } - - return nexthdr; -} - -int xfrm6_rcv(struct sk_buff *skb) +int xfrm6_rcv(struct sk_buff *skb, u8 proto) { int err; u32 spi, seq; @@ -177,32 +69,10 @@ struct xfrm_state *x; int xfrm_nr = 0; int decaps = 0; - struct ipv6hdr *hdr = skb->nh.ipv6h; - unsigned char *tmp_hdr = NULL; - int hdr_len = 0; 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; - } - - tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); - if (!tmp_hdr) - goto drop; - memcpy(tmp_hdr, skb->nh.raw, hdr_len); - - nexthdr = xfrm6_clear_mutable_options(skb, &nh_offset, XFRM_POLICY_IN); - hdr->priority = 0; - hdr->flow_lbl[0] = 0; - hdr->flow_lbl[1] = 0; - hdr->flow_lbl[2] = 0; - hdr->hop_limit = 0; - - if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) != 0) + if ((err = xfrm6_parse_spi(skb, proto, &spi, &seq)) != 0) goto drop; do { @@ -211,9 +81,10 @@ if (xfrm_nr == XFRM_MAX_DEPTH) goto drop; - x = xfrm6_state_lookup(&iph->daddr, spi, nexthdr); + x = xfrm6_state_lookup(&iph->daddr, spi, proto); if (x == NULL) goto drop; + spin_lock(&x->lock); if (unlikely(x->km.state != XFRM_STATE_VALID)) goto drop_unlock; @@ -221,8 +92,8 @@ if (x->props.replay_window && xfrm_replay_check(x, seq)) goto drop_unlock; - nexthdr = x->type->input(x, skb); - if (nexthdr <= 0) + proto = x->type->input(x, skb); + if (proto <= 0) goto drop_unlock; if (x->props.replay_window) @@ -237,13 +108,13 @@ iph = skb->nh.ipv6h; /* ??? */ - if (nexthdr == NEXTHDR_DEST) { + if (proto == 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]; + proto = 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; @@ -258,14 +129,10 @@ break; } - if ((err = xfrm6_parse_spi(skb, nexthdr, &spi, &seq)) < 0) + if ((err = xfrm6_parse_spi(skb, proto, &spi, &seq)) < 0) goto drop; } while (!err); - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - skb->nh.raw[nh_offset] = nexthdr; - skb->nh.ipv6h->payload_len = htons(hdr_len + skb->len - sizeof(struct ipv6hdr)); - /* Allocate new secpath or COW existing one. */ if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) { struct sec_path *sp; @@ -295,14 +162,13 @@ netif_rx(skb); return 0; } else { - return -nexthdr; + return -proto; } drop_unlock: spin_unlock(&x->lock); xfrm_state_put(x); drop: - if (tmp_hdr) kfree(tmp_hdr); while (--xfrm_nr >= 0) xfrm_state_put(xfrm_vec[xfrm_nr]); kfree_skb(skb); diff -ruN -x '*.o' -x '*.cmd' -x '*.ko' -x '*.mod.c' linux-2.5.62.orig/net/ipv6/xfrm_policy.c linux-2.5.62/net/ipv6/xfrm_policy.c --- linux-2.5.62.orig/net/ipv6/xfrm_policy.c 2003-02-19 14:24:53.000000000 -0800 +++ linux-2.5.62/net/ipv6/xfrm_policy.c 2003-02-19 02:50:41.000000000 -0800 @@ -229,7 +229,7 @@ read_lock_bh(&xfrm_policy_lock); for (pol = xfrm_policy_list[dir]; pol; pol = pol->next) { struct xfrm_selector *sel = &pol->selector; - if (pol->family != AF_INET6) continue); + if (pol->family != AF_INET6) continue; if (xfrm6_selector_match(sel, fl)) { atomic_inc(&pol->refcnt); break;