diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 20be8beb9a111dd0ca174784e0561cea90d189c7..92a1fc46ea593c6cfb2f0529568c4aa2e83a7c48 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -952,8 +952,8 @@ extern int xfrm6_rcv_spi(struct sk_buff *skb, __be32 spi); extern int xfrm6_rcv(struct sk_buff **pskb); extern int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto); -extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler); -extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler); +extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); +extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); extern __be32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr); extern void xfrm6_tunnel_free_spi(xfrm_address_t *saddr); extern __be32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 367b7483298610460e8449904200a1e5c3e0faee..662edb826899e5c78ba6f219699a8c4ac9199f4e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1128,7 +1128,7 @@ static int __init ip6_tunnel_init(void) { int err; - if (xfrm6_tunnel_register(&ip6ip6_handler)) { + if (xfrm6_tunnel_register(&ip6ip6_handler, AF_INET6)) { printk(KERN_ERR "ip6ip6 init: can't register tunnel\n"); return -EAGAIN; } @@ -1147,7 +1147,7 @@ static int __init ip6_tunnel_init(void) } return 0; fail: - xfrm6_tunnel_deregister(&ip6ip6_handler); + xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6); return err; } @@ -1171,7 +1171,7 @@ static void __exit ip6ip6_destroy_tunnels(void) static void __exit ip6_tunnel_cleanup(void) { - if (xfrm6_tunnel_deregister(&ip6ip6_handler)) + if (xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6)) printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); rtnl_lock(); diff --git a/net/ipv6/tunnel6.c b/net/ipv6/tunnel6.c index 918d07dd12197f7da36820cd8010e0192a1cb4a4..23e2809878ae87a82e527415b399d80b493103d8 100644 --- a/net/ipv6/tunnel6.c +++ b/net/ipv6/tunnel6.c @@ -30,9 +30,10 @@ #include static struct xfrm6_tunnel *tunnel6_handlers; +static struct xfrm6_tunnel *tunnel46_handlers; static DEFINE_MUTEX(tunnel6_mutex); -int xfrm6_tunnel_register(struct xfrm6_tunnel *handler) +int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family) { struct xfrm6_tunnel **pprev; int ret = -EEXIST; @@ -40,7 +41,8 @@ int xfrm6_tunnel_register(struct xfrm6_tunnel *handler) mutex_lock(&tunnel6_mutex); - for (pprev = &tunnel6_handlers; *pprev; pprev = &(*pprev)->next) { + for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; + *pprev; pprev = &(*pprev)->next) { if ((*pprev)->priority > priority) break; if ((*pprev)->priority == priority) @@ -60,14 +62,15 @@ int xfrm6_tunnel_register(struct xfrm6_tunnel *handler) EXPORT_SYMBOL(xfrm6_tunnel_register); -int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler) +int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family) { struct xfrm6_tunnel **pprev; int ret = -ENOENT; mutex_lock(&tunnel6_mutex); - for (pprev = &tunnel6_handlers; *pprev; pprev = &(*pprev)->next) { + for (pprev = (family == AF_INET6) ? &tunnel6_handlers : &tunnel46_handlers; + *pprev; pprev = &(*pprev)->next) { if (*pprev == handler) { *pprev = handler->next; ret = 0; @@ -103,6 +106,25 @@ static int tunnel6_rcv(struct sk_buff **pskb) return 0; } +static int tunnel46_rcv(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct xfrm6_tunnel *handler; + + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto drop; + + for (handler = tunnel46_handlers; handler; handler = handler->next) + if (!handler->handler(skb)) + return 0; + + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, skb->dev); + +drop: + kfree_skb(skb); + return 0; +} + static void tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, int type, int code, int offset, __be32 info) { @@ -119,17 +141,30 @@ static struct inet6_protocol tunnel6_protocol = { .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; +static struct inet6_protocol tunnel46_protocol = { + .handler = tunnel46_rcv, + .err_handler = tunnel6_err, + .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, +}; + static int __init tunnel6_init(void) { if (inet6_add_protocol(&tunnel6_protocol, IPPROTO_IPV6)) { printk(KERN_ERR "tunnel6 init(): can't add protocol\n"); return -EAGAIN; } + if (inet6_add_protocol(&tunnel46_protocol, IPPROTO_IPIP)) { + printk(KERN_ERR "tunnel6 init(): can't add protocol\n"); + inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6); + return -EAGAIN; + } return 0; } static void __exit tunnel6_fini(void) { + if (inet6_del_protocol(&tunnel46_protocol, IPPROTO_IPIP)) + printk(KERN_ERR "tunnel6 close: can't remove protocol\n"); if (inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6)) printk(KERN_ERR "tunnel6 close: can't remove protocol\n"); } diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 25250147bdc3514c21a92332a07e0f100383f8cd..31f651f950967018be3c43d0b173dd80dc0449c0 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -40,7 +40,8 @@ int xfrm6_rcv_spi(struct sk_buff *skb, __be32 spi) if (xfrm_nr == XFRM_MAX_DEPTH) goto drop; - x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, nexthdr, AF_INET6); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, spi, + nexthdr != IPPROTO_IPIP ? nexthdr : IPPROTO_IPV6, AF_INET6); if (x == NULL) goto drop; spin_lock(&x->lock); diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index fb0228772f0194b7aad847c0e38093d561821213..ee4b84a33ff41a63a22fd7cb628e5e2e382e737c 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -339,17 +339,29 @@ static struct xfrm6_tunnel xfrm6_tunnel_handler = { .priority = 2, }; +static struct xfrm6_tunnel xfrm46_tunnel_handler = { + .handler = xfrm6_tunnel_rcv, + .err_handler = xfrm6_tunnel_err, + .priority = 2, +}; + static int __init xfrm6_tunnel_init(void) { if (xfrm_register_type(&xfrm6_tunnel_type, AF_INET6) < 0) return -EAGAIN; - if (xfrm6_tunnel_register(&xfrm6_tunnel_handler)) { + if (xfrm6_tunnel_register(&xfrm6_tunnel_handler, AF_INET6)) { + xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); + return -EAGAIN; + } + if (xfrm6_tunnel_register(&xfrm46_tunnel_handler, AF_INET)) { + xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); return -EAGAIN; } if (xfrm6_tunnel_spi_init() < 0) { - xfrm6_tunnel_deregister(&xfrm6_tunnel_handler); + xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); + xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); return -EAGAIN; } @@ -359,7 +371,8 @@ static int __init xfrm6_tunnel_init(void) static void __exit xfrm6_tunnel_fini(void) { xfrm6_tunnel_spi_fini(); - xfrm6_tunnel_deregister(&xfrm6_tunnel_handler); + xfrm6_tunnel_deregister(&xfrm46_tunnel_handler, AF_INET); + xfrm6_tunnel_deregister(&xfrm6_tunnel_handler, AF_INET6); xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); }