From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mitsuru KANDA / =?ISO-2022-JP?B?GyRCP0BFRBsoQiAbJEI9PBsoQg==?= Subject: [PATCH] xfrm ip6ip6 (revised) Date: Sat, 14 Jun 2003 01:03:50 +0900 Sender: netdev-bounce@oss.sgi.com Message-ID: <87smqerml5.wl@karaba.org> References: <87fzmv5ejc.wl@karaba.org> <20030601.013040.116362760.davem@redhat.com> Mime-Version: 1.0 (generated by SEMI 1.14.4 - "Hosorogi") Content-Type: text/plain; charset=US-ASCII Cc: jmorris@intercode.com.au, kuznet@ms2.inr.ac.ru, netdev@oss.sgi.com, usagi@linux-ipv6.org Return-path: To: "David S. Miller" In-Reply-To: <20030601.013040.116362760.davem@redhat.com> Errors-to: netdev-bounce@oss.sgi.com List-Id: netdev.vger.kernel.org Hello, I recreated a xfrm ip6ip6 patch. The changes to the previous patch are: - to allocate unique spi values in xfrm6_tunnel.c by using just simple open addressing hash, - to introduce device-like ip6ip6 handling, and - to fix some bugs. This patch is against CS1.1304. Chould you check this? Regards, -mk At Sun, 01 Jun 2003 01:30:40 -0700 (PDT), "David S. Miller" wrote: (snipped) > I would suggest following implementation: > > 1) Implement something similar to xfrm_alloc_spi(t, 1, ~(u32)0) > > It just needs to allocate unique SPI numbers local to > xfrm6_tunnel.c We mark "SPI" value zero as reserved and > to indicate failed lookup. > > 2) Create hash table, it is keyed by ipv6 address and hash table > entries give SPI values. > > So on input you would say something like: > > u32 spi; > > spi = spihash_lookup(&iph->saddr); > if (!spi) > goto drop; > x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, > IPPROTO_IPV6, AF_INET6); (snipped) Index: linux25-XFRM6_TUNNEL-20030612/include/net/xfrm.h =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/xfrm.h,v retrieving revision 1.1.1.25 retrieving revision 1.1.1.25.2.1 diff -u -r1.1.1.25 -r1.1.1.25.2.1 --- linux25-XFRM6_TUNNEL-20030612/include/net/xfrm.h 10 Jun 2003 13:21:39 -0000 1.1.1.25 +++ linux25-XFRM6_TUNNEL-20030612/include/net/xfrm.h 12 Jun 2003 15:30:16 -0000 1.1.1.25.2.1 @@ -494,10 +494,6 @@ return 0; } -/* placeholder until xfrm6_tunnel.c is written */ -static inline int xfrm6_tunnel_check_size(struct sk_buff *skb) -{ return 0; } - /* A struct encoding bundle of transformations to apply to some set of flow. * * dst->child points to the next element of bundle. @@ -748,6 +744,12 @@ void (*err_handler)(struct sk_buff *skb, void *info); }; +struct xfrm6_tunnel { + int (*handler)(struct sk_buff **pskb, unsigned int *nhoffp); + void (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info); +}; + extern void xfrm_init(void); extern void xfrm4_init(void); extern void xfrm4_fini(void); @@ -781,6 +783,11 @@ extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler); extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler); extern int xfrm4_tunnel_check_size(struct sk_buff *skb); +extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler); +extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler); +extern int xfrm6_tunnel_check_size(struct sk_buff *skb); +extern u32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr); +extern u32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr); extern int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp); 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); Index: linux25-XFRM6_TUNNEL-20030612/net/ipv6/Makefile =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/Makefile,v retrieving revision 1.1.1.13 retrieving revision 1.1.1.13.2.1 diff -u -r1.1.1.13 -r1.1.1.13.2.1 --- linux25-XFRM6_TUNNEL-20030612/net/ipv6/Makefile 10 Jun 2003 13:21:55 -0000 1.1.1.13 +++ linux25-XFRM6_TUNNEL-20030612/net/ipv6/Makefile 12 Jun 2003 15:29:53 -0000 1.1.1.13.2.1 @@ -9,7 +9,7 @@ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ ip6_flowlabel.o ipv6_syms.o \ - xfrm6_policy.o xfrm6_state.o xfrm6_input.o + xfrm6_policy.o xfrm6_state.o xfrm6_input.o xfrm6_tunnel.o obj-$(CONFIG_INET6_AH) += ah6.o obj-$(CONFIG_INET6_ESP) += esp6.o Index: linux25-XFRM6_TUNNEL-20030612/net/ipv6/ip6_tunnel.c =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ip6_tunnel.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.2.1 diff -u -r1.1.1.1 -r1.1.1.1.2.1 --- linux25-XFRM6_TUNNEL-20030612/net/ipv6/ip6_tunnel.c 10 Jun 2003 13:21:55 -0000 1.1.1.1 +++ linux25-XFRM6_TUNNEL-20030612/net/ipv6/ip6_tunnel.c 12 Jun 2003 15:29:53 -0000 1.1.1.1.2.1 @@ -48,6 +48,7 @@ #include #include #include +#include MODULE_AUTHOR("Ville Nuorvala"); MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); @@ -1174,10 +1175,9 @@ return 0; } -static struct inet6_protocol ip6ip6_protocol = { +static struct xfrm_tunnel ip6ip6_handler = { .handler = ip6ip6_rcv, .err_handler = ip6ip6_err, - .flags = INET6_PROTO_FINAL }; /** @@ -1192,6 +1192,11 @@ struct sock *sk; struct ipv6_pinfo *np; + if (xfrm6_tunnel_register(&ip6ip6_handler) < 0) { + printk(KERN_INFO "ip6ip6 init: can't register tunnel\n"); + return -EAGAIN; + } + ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl; for (i = 0; i < NR_CPUS; i++) { @@ -1216,10 +1221,6 @@ sk->sk_prot->unhash(sk); } - if ((err = inet6_add_protocol(&ip6ip6_protocol, IPPROTO_IPV6)) < 0) { - printk(KERN_ERR "Failed to register IPv6 protocol\n"); - goto fail; - } SET_MODULE_OWNER(&ip6ip6_fb_tnl_dev); register_netdev(&ip6ip6_fb_tnl_dev); @@ -1243,9 +1244,10 @@ { int i; - unregister_netdev(&ip6ip6_fb_tnl_dev); + if (xfrm6_tunnel_deregister(&ip6ip6_handler) < 0) + printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); - inet6_del_protocol(&ip6ip6_protocol, IPPROTO_IPV6); + unregister_netdev(&ip6ip6_fb_tnl_dev); for (i = 0; i < NR_CPUS; i++) { if (!cpu_possible(i)) Index: linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipcomp6.c =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ipcomp6.c,v retrieving revision 1.1.1.3 diff -u -r1.1.1.3 ipcomp6.c --- linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipcomp6.c 10 Jun 2003 13:21:54 -0000 1.1.1.3 +++ linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipcomp6.c 13 Jun 2003 09:44:59 -0000 @@ -254,6 +254,66 @@ xfrm_state_put(x); } +static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) +{ + struct xfrm_state *t = NULL; + + t = xfrm_state_alloc(); + if (!t) + goto out; + + t->id.proto = IPPROTO_IPV6; + t->id.spi = xfrm6_tunnel_alloc_spi((xfrm_address_t *)&x->props.saddr); + memcpy(t->id.daddr.a6, x->id.daddr.a6, sizeof(struct in6_addr)); + memcpy(&t->sel, &x->sel, sizeof(t->sel)); + t->props.family = AF_INET6; + t->props.mode = 1; + memcpy(t->props.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr)); + + t->type = xfrm_get_type(IPPROTO_IPV6, t->props.family); + if (t->type == NULL) + goto error; + + if (t->type->init_state(t, NULL)) + goto error; + + t->km.state = XFRM_STATE_VALID; + atomic_set(&t->tunnel_users, 1); + +out: + return t; + +error: + xfrm_state_put(t); + goto out; +} + +static int ipcomp6_tunnel_attach(struct xfrm_state *x) +{ + int err = 0; + struct xfrm_state *t = NULL; + u32 spi; + + spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&x->props.saddr); + if (spi) + t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr, + spi, IPPROTO_IPV6, AF_INET6); + if (!t) { + t = ipcomp6_tunnel_create(x); + if (!t) { + err = -EINVAL; + goto out; + } + xfrm_state_insert(t); + xfrm_state_hold(t); + } + x->tunnel = t; + atomic_inc(&t->tunnel_users); + +out: + return err; +} + static void ipcomp6_free_data(struct ipcomp_data *ipcd) { if (ipcd->tfm) @@ -292,6 +352,12 @@ ipcd->tfm = crypto_alloc_tfm(x->calg->alg_name, 0); if (!ipcd->tfm) goto error; + + if (x->props.mode) { + err = ipcomp6_tunnel_attach(x); + if (err) + goto error; + } calg_desc = xfrm_calg_get_byname(x->calg->alg_name); BUG_ON(!calg_desc); Index: linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipv6_syms.c =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ipv6_syms.c,v retrieving revision 1.1.1.13 retrieving revision 1.1.1.13.2.1 diff -u -r1.1.1.13 -r1.1.1.13.2.1 --- linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipv6_syms.c 10 Jun 2003 13:21:55 -0000 1.1.1.13 +++ linux25-XFRM6_TUNNEL-20030612/net/ipv6/ipv6_syms.c 12 Jun 2003 15:29:53 -0000 1.1.1.13.2.1 @@ -38,6 +38,11 @@ EXPORT_SYMBOL(ip6_find_1stfragopt); EXPORT_SYMBOL(xfrm6_rcv); EXPORT_SYMBOL(xfrm6_clear_mutable_options); +EXPORT_SYMBOL(xfrm6_tunnel_register); +EXPORT_SYMBOL(xfrm6_tunnel_deregister); +EXPORT_SYMBOL(xfrm6_tunnel_check_size); +EXPORT_SYMBOL(xfrm6_tunnel_alloc_spi); +EXPORT_SYMBOL(xfrm6_tunnel_spi_lookup); EXPORT_SYMBOL(rt6_lookup); EXPORT_SYMBOL(fl6_sock_lookup); EXPORT_SYMBOL(ipv6_ext_hdr); Index: linux25-XFRM6_TUNNEL-20030612/net/ipv6/xfrm6_tunnel.c =================================================================== RCS file: linux25-XFRM6_TUNNEL-20030612/net/ipv6/xfrm6_tunnel.c diff -N linux25-XFRM6_TUNNEL-20030612/net/ipv6/xfrm6_tunnel.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ linux25-XFRM6_TUNNEL-20030612/net/ipv6/xfrm6_tunnel.c 13 Jun 2003 09:44:59 -0000 @@ -0,0 +1,380 @@ +/* + * Copyright (C)2003 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author Mitsuru KANDA + * + * Based on xfrm4_tunnel + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XFRM6_TUNNEL_HSIZE 1024 +/* note: we assume index of xfrm_tunnel_table[] == spi */ +static xfrm_address_t *xfrm6_tunnel_table[XFRM6_TUNNEL_HSIZE]; + +static spinlock_t xfrm6_tunnel_lock = SPIN_LOCK_UNLOCKED; + +static unsigned xfrm6_addr_hash(xfrm_address_t *addr) +{ + unsigned h; + h = ntohl(addr->a6[0]^addr->a6[1]^addr->a6[2]^addr->a6[3]); + h = (h ^ (h>>16)) % XFRM6_TUNNEL_HSIZE; + return h; +} + +static void xfrm6_tunnel_htable_init(void) +{ + int i; + for (i=0; idst; + + mtu = dst_pmtu(dst) - sizeof(struct ipv6hdr); + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + if (skb->len > mtu) { + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); + ret = -EMSGSIZE; + } + + return ret; +} + +static int ip6ip6_output(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct xfrm_state *x = dst->xfrm; + struct ipv6hdr *iph, *top_iph; + int err; + + if ((err = xfrm6_tunnel_check_size(skb)) != 0) + goto error_nolock; + + iph = skb->nh.ipv6h; + + top_iph = (struct ipv6hdr *)skb_push(skb, x->props.header_len); + top_iph->version = 6; + top_iph->priority = iph->priority; + top_iph->flow_lbl[0] = iph->flow_lbl[0]; + top_iph->flow_lbl[1] = iph->flow_lbl[1]; + top_iph->flow_lbl[2] = iph->flow_lbl[2]; + top_iph->nexthdr = IPPROTO_IPV6; + top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + top_iph->hop_limit = iph->hop_limit; + ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); + ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); + skb->nh.raw = skb->data; + skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr); + + x->curlft.bytes += skb->len; + x->curlft.packets++; + + spin_unlock_bh(&x->lock); + + if ((skb->dst = dst_pop(dst)) == NULL) { + kfree_skb(skb); + err = -EHOSTUNREACH; + goto error_nolock; + } + + return NET_XMIT_BYPASS; + +error_nolock: + kfree_skb(skb); + return err; +} + +static int ip6ip6_xfrm_rcv(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + return -EINVAL; + + skb->mac.raw = skb->nh.raw; + skb->nh.raw = skb->data; + dst_release(skb->dst); + skb->dst = NULL; + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + netif_rx(skb); + + return 0; +} + +static struct xfrm6_tunnel *ip6ip6_handler; +static DECLARE_MUTEX(xfrm6_tunnel_sem); + +int xfrm6_tunnel_register(struct xfrm6_tunnel *handler) +{ + int ret; + + down(&xfrm6_tunnel_sem); + ret = 0; + if (ip6ip6_handler != NULL) + ret = -EINVAL; + if (!ret) + ip6ip6_handler = handler; + up(&xfrm6_tunnel_sem); + + return ret; +} + +int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler) +{ + int ret; + + down(&xfrm6_tunnel_sem); + ret = 0; + if (ip6ip6_handler != handler) + ret = -EINVAL; + if (!ret) + ip6ip6_handler = NULL; + up(&xfrm6_tunnel_sem); + + synchronize_net(); + + return ret; +} + +static int ip6ip6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) +{ + struct sk_buff *skb = *pskb; + struct xfrm6_tunnel *handler = ip6ip6_handler; + struct xfrm_state *x = NULL; + struct ipv6hdr *iph = skb->nh.ipv6h; + int err = 0; + u32 spi; + + /* device-like_ip6ip6_handler() */ + if (handler) { + err = handler->handler(pskb, nhoffp); + if (!err) + goto out; + } + + spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, + spi, + IPPROTO_IPV6, AF_INET6); + + if (!x) + goto drop; + + spin_lock(&x->lock); + + if (unlikely(x->km.state != XFRM_STATE_VALID)) + goto drop_unlock; + + err = ip6ip6_xfrm_rcv(x, NULL, skb); + if (err) + goto drop_unlock; + + x->curlft.bytes += skb->len; + x->curlft.packets++; + spin_unlock(&x->lock); + xfrm_state_put(x); + +out: + return 0; + +drop_unlock: + spin_unlock(&x->lock); + xfrm_state_put(x); +drop: + kfree_skb(skb); + + return -1; +} + +static void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + struct xfrm6_tunnel *handler = ip6ip6_handler; + + /* call here first for device-like ip6ip6 err handling */ + if (handler) { + handler->err_handler(skb, opt, type, code, offset, info); + return; + } + + /* xfrm ip6ip6 native err handling */ + switch (type) { + case ICMPV6_DEST_UNREACH: + switch (code) { + case ICMPV6_NOROUTE: + case ICMPV6_ADM_PROHIBITED: + case ICMPV6_NOT_NEIGHBOUR: + case ICMPV6_ADDR_UNREACH: + case ICMPV6_PORT_UNREACH: + default: + printk(KERN_ERR "xfrm ip6ip6: Destination Unreach.\n"); + break; + } + break; + case ICMPV6_PKT_TOOBIG: + printk(KERN_ERR "xfrm ip6ip6: Packet Too Big.\n"); + break; + case ICMPV6_TIME_EXCEED: + switch (code) { + case ICMPV6_EXC_HOPLIMIT: + printk(KERN_ERR "xfrm ip6ip6: Too small Hoplimit.\n"); + break; + case ICMPV6_EXC_FRAGTIME: + default: + break; + } + break; + case ICMPV6_PARAMPROB: + switch (code) { + case ICMPV6_HDR_FIELD: break; + case ICMPV6_UNK_NEXTHDR: break; + case ICMPV6_UNK_OPTION: break; + } + break; + default: + break; + } + return; +} + +static int ip6ip6_init_state(struct xfrm_state *x, void *args) +{ + if (!x->props.mode) + return -EINVAL; + + x->props.header_len = sizeof(struct ipv6hdr); + + return 0; +} + +static void ip6ip6_destroy(struct xfrm_state *x) +{ + xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr); +} + +static struct xfrm_type ip6ip6_type = { + .description = "IP6IP6", + .owner = THIS_MODULE, + .proto = IPPROTO_IPV6, + .init_state = ip6ip6_init_state, + .destructor = ip6ip6_destroy, + .input = ip6ip6_xfrm_rcv, + .output = ip6ip6_output, +}; + +static struct inet6_protocol ip6ip6_protocol = { + .handler = ip6ip6_rcv, + .err_handler = ip6ip6_err, + .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, +}; + +static int __init ip6ip6_init(void) +{ + if (xfrm_register_type(&ip6ip6_type, AF_INET6) < 0) { + printk(KERN_INFO "ip6ip6 init: can't add xfrm type\n"); + return -EAGAIN; + } + if (inet6_add_protocol(&ip6ip6_protocol, IPPROTO_IPV6) < 0) { + printk(KERN_INFO "ip6ip6 init: can't add protocol\n"); + xfrm_unregister_type(&ip6ip6_type, AF_INET6); + return -EAGAIN; + } + xfrm6_tunnel_htable_init(); + return 0; +} + +static void __exit ip6ip6_fini(void) +{ + if (inet6_del_protocol(&ip6ip6_protocol, IPPROTO_IPV6) < 0) + printk(KERN_INFO "ip6ip6 close: can't remove protocol\n"); + if (xfrm_unregister_type(&ip6ip6_type, AF_INET6) < 0) + printk(KERN_INFO "ip6ip6 close: can't remove xfrm type\n"); +} + +module_init(ip6ip6_init); +module_exit(ip6ip6_fini); +MODULE_LICENSE("GPL"); Index: linux25-XFRM6_TUNNEL-20030612/net/xfrm/xfrm_output.c =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/xfrm/xfrm_output.c,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.24.1 diff -u -r1.1.1.1 -r1.1.1.1.24.1 --- linux25-XFRM6_TUNNEL-20030612/net/xfrm/xfrm_output.c 6 May 2003 12:43:55 -0000 1.1.1.1 +++ linux25-XFRM6_TUNNEL-20030612/net/xfrm/xfrm_output.c 12 Jun 2003 15:29:06 -0000 1.1.1.1.24.1 @@ -27,11 +27,11 @@ case AF_INET: err = xfrm4_tunnel_check_size(skb); break; - +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: err = xfrm6_tunnel_check_size(skb); break; - +#endif default: err = -EINVAL; }