From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mitsuru KANDA / =?ISO-2022-JP?B?GyRCP0BFRBsoQiAbJEI9PBsoQg==?= Subject: [PATCH] xfrm ip6ip6 Date: Sun, 01 Jun 2003 00:20:07 +0900 Sender: netdev-bounce@oss.sgi.com Message-ID: <87fzmv5ejc.wl@karaba.org> Mime-Version: 1.0 (generated by SEMI 1.14.4 - "Hosorogi") Content-Type: text/plain; charset=US-ASCII Cc: netdev@oss.sgi.com, usagi@linux-ipv6.org Return-path: To: davem@redhat.com, jmorris@intercode.com.au, kuznet@ms2.inr.ac.ru Errors-to: netdev-bounce@oss.sgi.com List-Id: netdev.vger.kernel.org Hello, Here is the xfrm style ip6ip6 patch, which is against linux-2.5.70+CS1.1254.2.15. I honestly have to say it is not tested well. But I promise I'll test/fix after xfrm tunnel tool will be visible :-) (Does anyone work on this?) Anyway, I have one discussion point. When we use 'setkey -D', xfrm ipip tunnels are displayed as unspec SAs. Do we need to filter out these xfrm_states other than AH/ESP/IPcomp in case of PF_KEY based queries? Regards, -mk Index: linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/include/net/xfrm.h =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/xfrm.h,v retrieving revision 1.1.1.23 retrieving revision 1.1.1.23.10.1 diff -u -r1.1.1.23 -r1.1.1.23.10.1 --- linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/include/net/xfrm.h 26 May 2003 08:04:25 -0000 1.1.1.23 +++ linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/include/net/xfrm.h 31 May 2003 08:52:20 -0000 1.1.1.23.10.1 @@ -493,8 +493,16 @@ } /* placeholder until xfrm6_tunnel.c is written */ -static inline int xfrm6_tunnel_check_size(struct sk_buff *skb) -{ return 0; } +extern int xfrm6_tunnel_check_size(struct sk_buff *skb); +#define XFRM_ADDR_HSIZE 1024 +static __inline__ /* same as __xfrm6_dst_hash */ +unsigned xfrm6_tunnel_addr_hash(xfrm_address_t *addr) +{ + unsigned h; + h = ntohl(addr->a6[2]^addr->a6[3]); + h = (h ^ (h>>16)) % XFRM_DST_HSIZE; + return h; +} /* A struct encoding bundle of transformations to apply to some set of flow. * Index: linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/Makefile =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/Makefile,v retrieving revision 1.1.1.12 retrieving revision 1.1.1.12.10.1 diff -u -r1.1.1.12 -r1.1.1.12.10.1 --- linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/Makefile 26 May 2003 08:04:11 -0000 1.1.1.12 +++ linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/Makefile 31 May 2003 08:56:24 -0000 1.1.1.12.10.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-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/ipcomp6.c =================================================================== RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ipcomp6.c,v retrieving revision 1.1.1.2 retrieving revision 1.1.1.2.8.2 diff -u -r1.1.1.2 -r1.1.1.2.8.2 --- linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/ipcomp6.c 21 May 2003 13:15:20 -0000 1.1.1.2 +++ linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/ipcomp6.c 31 May 2003 14:19:54 -0000 1.1.1.2.8.2 @@ -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_addr_hash((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; + u32 spi; + + spi = xfrm6_tunnel_addr_hash((xfrm_address_t *)&x->props.saddr); + + 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-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/xfrm6_tunnel.c =================================================================== RCS file: linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/xfrm6_tunnel.c diff -N linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/xfrm6_tunnel.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/ipv6/xfrm6_tunnel.c 31 May 2003 14:03:12 -0000 1.1.4.2 @@ -0,0 +1,259 @@ +/* + * 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 + +int xfrm6_tunnel_check_size(struct sk_buff *skb) +{ + int mtu, ret = 0; + struct dst_entry *dst = skb->dst; + + 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; + memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr)); + memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr)); + 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 int ip6ip6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) +{ + struct sk_buff *skb = *pskb; + struct xfrm_state *x = NULL; + struct ipv6hdr *iph = skb->nh.ipv6h; + int err = 0; + u32 spi; + + /* After device-like ip6ip6 tunnel will be implemented, + * we do take precedence here. + * (currently not yet exist in kernel source tree.) + * For more information, see xfrm4_tunnel.c + */ + /* device-like_ip6ip6_handler() */ + + spi = xfrm6_tunnel_addr_hash((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); + + *nhoffp = sizeof(struct ipv6hdr); + + return 1; + +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) +{ + /* call here first for device-like ip6ip6 err handling */ + /* foo->err_handler(), see xfrm4_tunnel.c */ + + /* 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) +{ +} + +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, +}; + +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; + } + 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-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/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.16.1 diff -u -r1.1.1.1 -r1.1.1.1.16.1 --- linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/xfrm/xfrm_output.c 6 May 2003 12:43:55 -0000 1.1.1.1 +++ linux25-b2_5_70+CS1_1254_2_15_XFRM6_TUNNEL_20030531/net/xfrm/xfrm_output.c 31 May 2003 08:54:21 -0000 1.1.1.1.16.1 @@ -28,9 +28,11 @@ 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;