summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/xfrm.h16
-rw-r--r--net/ipv4/Kconfig17
-rw-r--r--net/ipv4/Makefile3
-rw-r--r--net/ipv4/ipip.c79
-rw-r--r--net/ipv4/tunnel4.c113
-rw-r--r--net/ipv4/xfrm4_tunnel.c79
-rw-r--r--net/ipv6/Kconfig19
-rw-r--r--net/ipv6/Makefile3
-rw-r--r--net/ipv6/ip6_tunnel.c45
-rw-r--r--net/ipv6/tunnel6.c131
-rw-r--r--net/ipv6/xfrm6_input.c5
-rw-r--r--net/ipv6/xfrm6_tunnel.c81
12 files changed, 344 insertions, 247 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 61b7504fc2ba..e100291e43f4 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -864,13 +864,19 @@ struct xfrm_algo_desc {
/* XFRM tunnel handlers. */
struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb);
- void (*err_handler)(struct sk_buff *skb, __u32 info);
+ int (*err_handler)(struct sk_buff *skb, __u32 info);
+
+ struct xfrm_tunnel *next;
+ int priority;
};
struct xfrm6_tunnel {
- int (*handler)(struct sk_buff **pskb);
- void (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
- int type, int code, int offset, __u32 info);
+ int (*handler)(struct sk_buff *skb);
+ int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info);
+
+ struct xfrm6_tunnel *next;
+ int priority;
};
extern void xfrm_init(void);
@@ -906,7 +912,7 @@ extern int xfrm4_rcv(struct sk_buff *skb);
extern int xfrm4_output(struct sk_buff *skb);
extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler);
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler);
-extern int xfrm6_rcv_spi(struct sk_buff **pskb, u32 spi);
+extern int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi);
extern int xfrm6_rcv(struct sk_buff **pskb);
extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler);
extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler);
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 011cca7ae02b..e40f75322377 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -235,6 +235,7 @@ config IP_PNP_RARP
# bool ' IP: ARP support' CONFIG_IP_PNP_ARP
config NET_IPIP
tristate "IP: tunneling"
+ select INET_TUNNEL
---help---
Tunneling means encapsulating data of one protocol type within
another protocol and sending it over a channel that understands the
@@ -395,7 +396,7 @@ config INET_ESP
config INET_IPCOMP
tristate "IP: IPComp transformation"
select XFRM
- select INET_TUNNEL
+ select INET_XFRM_TUNNEL
select CRYPTO
select CRYPTO_DEFLATE
---help---
@@ -404,14 +405,14 @@ config INET_IPCOMP
If unsure, say Y.
+config INET_XFRM_TUNNEL
+ tristate
+ select INET_TUNNEL
+ default n
+
config INET_TUNNEL
- tristate "IP: tunnel transformation"
- select XFRM
- ---help---
- Support for generic IP tunnel transformation, which is required by
- the IP tunneling module as well as tunnel mode IPComp.
-
- If unsure, say Y.
+ tristate
+ default n
config INET_DIAG
tristate "INET: socket monitoring interface"
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 35e5f5999092..9ef50a0b9d2c 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -22,7 +22,8 @@ obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah4.o
obj-$(CONFIG_INET_ESP) += esp4.o
obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
-obj-$(CONFIG_INET_TUNNEL) += xfrm4_tunnel.o
+obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o
+obj-$(CONFIG_INET_TUNNEL) += tunnel4.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 03d13742a4b8..eef07b0916a3 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -114,7 +114,6 @@
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
-#include <net/protocol.h>
#include <net/ipip.h>
#include <net/inet_ecn.h>
#include <net/xfrm.h>
@@ -274,7 +273,7 @@ static void ipip_tunnel_uninit(struct net_device *dev)
dev_put(dev);
}
-static void ipip_err(struct sk_buff *skb, u32 info)
+static int ipip_err(struct sk_buff *skb, u32 info)
{
#ifndef I_WISH_WORLD_WERE_PERFECT
@@ -286,21 +285,22 @@ static void ipip_err(struct sk_buff *skb, u32 info)
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
struct ip_tunnel *t;
+ int err;
switch (type) {
default:
case ICMP_PARAMETERPROB:
- return;
+ return 0;
case ICMP_DEST_UNREACH:
switch (code) {
case ICMP_SR_FAILED:
case ICMP_PORT_UNREACH:
/* Impossible event. */
- return;
+ return 0;
case ICMP_FRAG_NEEDED:
/* Soft state for pmtu is maintained by IP core. */
- return;
+ return 0;
default:
/* All others are translated to HOST_UNREACH.
rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -311,14 +311,18 @@ static void ipip_err(struct sk_buff *skb, u32 info)
break;
case ICMP_TIME_EXCEEDED:
if (code != ICMP_EXC_TTL)
- return;
+ return 0;
break;
}
+ err = -ENOENT;
+
read_lock(&ipip_lock);
t = ipip_tunnel_lookup(iph->daddr, iph->saddr);
if (t == NULL || t->parms.iph.daddr == 0)
goto out;
+
+ err = 0;
if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
goto out;
@@ -329,7 +333,7 @@ static void ipip_err(struct sk_buff *skb, u32 info)
t->err_time = jiffies;
out:
read_unlock(&ipip_lock);
- return;
+ return err;
#else
struct iphdr *iph = (struct iphdr*)dp;
int hlen = iph->ihl<<2;
@@ -344,15 +348,15 @@ out:
struct rtable *rt;
if (len < hlen + sizeof(struct iphdr))
- return;
+ return 0;
eiph = (struct iphdr*)(dp + hlen);
switch (type) {
default:
- return;
+ return 0;
case ICMP_PARAMETERPROB:
if (skb->h.icmph->un.gateway < hlen)
- return;
+ return 0;
/* So... This guy found something strange INSIDE encapsulated
packet. Well, he is fool, but what can we do ?
@@ -366,16 +370,16 @@ out:
case ICMP_SR_FAILED:
case ICMP_PORT_UNREACH:
/* Impossible event. */
- return;
+ return 0;
case ICMP_FRAG_NEEDED:
/* And it is the only really necessary thing :-) */
rel_info = ntohs(skb->h.icmph->un.frag.mtu);
if (rel_info < hlen+68)
- return;
+ return 0;
rel_info -= hlen;
/* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */
if (rel_info > ntohs(eiph->tot_len))
- return;
+ return 0;
break;
default:
/* All others are translated to HOST_UNREACH.
@@ -389,14 +393,14 @@ out:
break;
case ICMP_TIME_EXCEEDED:
if (code != ICMP_EXC_TTL)
- return;
+ return 0;
break;
}
/* Prepare fake skb to feed it to icmp_send */
skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2 == NULL)
- return;
+ return 0;
dst_release(skb2->dst);
skb2->dst = NULL;
skb_pull(skb2, skb->data - (u8*)eiph);
@@ -409,7 +413,7 @@ out:
fl.proto = IPPROTO_IPIP;
if (ip_route_output_key(&rt, &key)) {
kfree_skb(skb2);
- return;
+ return 0;
}
skb2->dev = rt->u.dst.dev;
@@ -424,14 +428,14 @@ out:
rt->u.dst.dev->type != ARPHRD_TUNNEL) {
ip_rt_put(rt);
kfree_skb(skb2);
- return;
+ return 0;
}
} else {
ip_rt_put(rt);
if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||
skb2->dst->dev->type != ARPHRD_TUNNEL) {
kfree_skb(skb2);
- return;
+ return 0;
}
}
@@ -439,7 +443,7 @@ out:
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
if (rel_info > dst_mtu(skb2->dst)) {
kfree_skb(skb2);
- return;
+ return 0;
}
skb2->dst->ops->update_pmtu(skb2->dst, rel_info);
rel_info = htonl(rel_info);
@@ -453,7 +457,7 @@ out:
icmp_send(skb2, rel_type, rel_code, rel_info);
kfree_skb(skb2);
- return;
+ return 0;
#endif
}
@@ -855,39 +859,12 @@ static int __init ipip_fb_tunnel_init(struct net_device *dev)
return 0;
}
-#ifdef CONFIG_INET_TUNNEL
static struct xfrm_tunnel ipip_handler = {
.handler = ipip_rcv,
.err_handler = ipip_err,
+ .priority = 1,
};
-static inline int ipip_register(void)
-{
- return xfrm4_tunnel_register(&ipip_handler);
-}
-
-static inline int ipip_unregister(void)
-{
- return xfrm4_tunnel_deregister(&ipip_handler);
-}
-#else
-static struct net_protocol ipip_protocol = {
- .handler = ipip_rcv,
- .err_handler = ipip_err,
- .no_policy = 1,
-};
-
-static inline int ipip_register(void)
-{
- return inet_add_protocol(&ipip_protocol, IPPROTO_IPIP);
-}
-
-static inline int ipip_unregister(void)
-{
- return inet_del_protocol(&ipip_protocol, IPPROTO_IPIP);
-}
-#endif
-
static char banner[] __initdata =
KERN_INFO "IPv4 over IPv4 tunneling driver\n";
@@ -897,7 +874,7 @@ static int __init ipip_init(void)
printk(banner);
- if (ipip_register() < 0) {
+ if (xfrm4_tunnel_register(&ipip_handler)) {
printk(KERN_INFO "ipip init: can't register tunnel\n");
return -EAGAIN;
}
@@ -919,7 +896,7 @@ static int __init ipip_init(void)
err2:
free_netdev(ipip_fb_tunnel_dev);
err1:
- ipip_unregister();
+ xfrm4_tunnel_deregister(&ipip_handler);
goto out;
}
@@ -939,7 +916,7 @@ static void __exit ipip_destroy_tunnels(void)
static void __exit ipip_fini(void)
{
- if (ipip_unregister() < 0)
+ if (xfrm4_tunnel_deregister(&ipip_handler))
printk(KERN_INFO "ipip close: can't deregister tunnel\n");
rtnl_lock();
diff --git a/net/ipv4/tunnel4.c b/net/ipv4/tunnel4.c
new file mode 100644
index 000000000000..0d7d386dac22
--- /dev/null
+++ b/net/ipv4/tunnel4.c
@@ -0,0 +1,113 @@
+/* tunnel4.c: Generic IP tunnel transformer.
+ *
+ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/xfrm.h>
+
+static struct xfrm_tunnel *tunnel4_handlers;
+static DEFINE_MUTEX(tunnel4_mutex);
+
+int xfrm4_tunnel_register(struct xfrm_tunnel *handler)
+{
+ struct xfrm_tunnel **pprev;
+ int ret = -EEXIST;
+ int priority = handler->priority;
+
+ mutex_lock(&tunnel4_mutex);
+
+ for (pprev = &tunnel4_handlers; *pprev; pprev = &(*pprev)->next) {
+ if ((*pprev)->priority > priority)
+ break;
+ if ((*pprev)->priority == priority)
+ goto err;
+ }
+
+ handler->next = *pprev;
+ *pprev = handler;
+
+ ret = 0;
+
+err:
+ mutex_unlock(&tunnel4_mutex);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(xfrm4_tunnel_register);
+
+int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler)
+{
+ struct xfrm_tunnel **pprev;
+ int ret = -ENOENT;
+
+ mutex_lock(&tunnel4_mutex);
+
+ for (pprev = &tunnel4_handlers; *pprev; pprev = &(*pprev)->next) {
+ if (*pprev == handler) {
+ *pprev = handler->next;
+ ret = 0;
+ break;
+ }
+ }
+
+ mutex_unlock(&tunnel4_mutex);
+
+ synchronize_net();
+
+ return ret;
+}
+
+EXPORT_SYMBOL(xfrm4_tunnel_deregister);
+
+static int tunnel4_rcv(struct sk_buff *skb)
+{
+ struct xfrm_tunnel *handler;
+
+ for (handler = tunnel4_handlers; handler; handler = handler->next)
+ if (!handler->handler(skb))
+ return 0;
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void tunnel4_err(struct sk_buff *skb, u32 info)
+{
+ struct xfrm_tunnel *handler;
+
+ for (handler = tunnel4_handlers; handler; handler = handler->next)
+ if (!handler->err_handler(skb, info))
+ break;
+}
+
+static struct net_protocol tunnel4_protocol = {
+ .handler = tunnel4_rcv,
+ .err_handler = tunnel4_err,
+ .no_policy = 1,
+};
+
+static int __init tunnel4_init(void)
+{
+ if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) {
+ printk(KERN_ERR "tunnel4 init: can't add protocol\n");
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+static void __exit tunnel4_fini(void)
+{
+ if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
+ printk(KERN_ERR "tunnel4 close: can't remove protocol\n");
+}
+
+module_init(tunnel4_init);
+module_exit(tunnel4_fini);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c
index b08d56b117f8..2d670935c2b5 100644
--- a/net/ipv4/xfrm4_tunnel.c
+++ b/net/ipv4/xfrm4_tunnel.c
@@ -26,64 +26,6 @@ static int ipip_xfrm_rcv(struct xfrm_state *x, struct xfrm_decap_state *decap, s
return 0;
}
-static struct xfrm_tunnel *ipip_handler;
-static DEFINE_MUTEX(xfrm4_tunnel_mutex);
-
-int xfrm4_tunnel_register(struct xfrm_tunnel *handler)
-{
- int ret;
-
- mutex_lock(&xfrm4_tunnel_mutex);
- ret = 0;
- if (ipip_handler != NULL)
- ret = -EINVAL;
- if (!ret)
- ipip_handler = handler;
- mutex_unlock(&xfrm4_tunnel_mutex);
-
- return ret;
-}
-
-EXPORT_SYMBOL(xfrm4_tunnel_register);
-
-int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler)
-{
- int ret;
-
- mutex_lock(&xfrm4_tunnel_mutex);
- ret = 0;
- if (ipip_handler != handler)
- ret = -EINVAL;
- if (!ret)
- ipip_handler = NULL;
- mutex_unlock(&xfrm4_tunnel_mutex);
-
- synchronize_net();
-
- return ret;
-}
-
-EXPORT_SYMBOL(xfrm4_tunnel_deregister);
-
-static int ipip_rcv(struct sk_buff *skb)
-{
- struct xfrm_tunnel *handler = ipip_handler;
-
- /* Tunnel devices take precedence. */
- if (handler && handler->handler(skb) == 0)
- return 0;
-
- return xfrm4_rcv(skb);
-}
-
-static void ipip_err(struct sk_buff *skb, u32 info)
-{
- struct xfrm_tunnel *handler = ipip_handler;
-
- if (handler)
- handler->err_handler(skb, info);
-}
-
static int ipip_init_state(struct xfrm_state *x)
{
if (!x->props.mode)
@@ -111,10 +53,15 @@ static struct xfrm_type ipip_type = {
.output = ipip_output
};
-static struct net_protocol ipip_protocol = {
- .handler = ipip_rcv,
- .err_handler = ipip_err,
- .no_policy = 1,
+static int xfrm_tunnel_err(struct sk_buff *skb, u32 info)
+{
+ return -ENOENT;
+}
+
+static struct xfrm_tunnel xfrm_tunnel_handler = {
+ .handler = xfrm4_rcv,
+ .err_handler = xfrm_tunnel_err,
+ .priority = 2,
};
static int __init ipip_init(void)
@@ -123,8 +70,8 @@ static int __init ipip_init(void)
printk(KERN_INFO "ipip init: can't add xfrm type\n");
return -EAGAIN;
}
- if (inet_add_protocol(&ipip_protocol, IPPROTO_IPIP) < 0) {
- printk(KERN_INFO "ipip init: can't add protocol\n");
+ if (xfrm4_tunnel_register(&xfrm_tunnel_handler)) {
+ printk(KERN_INFO "ipip init: can't add xfrm handler\n");
xfrm_unregister_type(&ipip_type, AF_INET);
return -EAGAIN;
}
@@ -133,8 +80,8 @@ static int __init ipip_init(void)
static void __exit ipip_fini(void)
{
- if (inet_del_protocol(&ipip_protocol, IPPROTO_IPIP) < 0)
- printk(KERN_INFO "ipip close: can't remove protocol\n");
+ if (xfrm4_tunnel_deregister(&xfrm_tunnel_handler))
+ printk(KERN_INFO "ipip close: can't remove xfrm handler\n");
if (xfrm_unregister_type(&ipip_type, AF_INET) < 0)
printk(KERN_INFO "ipip close: can't remove xfrm type\n");
}
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index e6f83b6a2b76..f8a107ab5592 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -88,7 +88,7 @@ config INET6_IPCOMP
tristate "IPv6: IPComp transformation"
depends on IPV6
select XFRM
- select INET6_TUNNEL
+ select INET6_XFRM_TUNNEL
select CRYPTO
select CRYPTO_DEFLATE
---help---
@@ -97,19 +97,18 @@ config INET6_IPCOMP
If unsure, say Y.
+config INET6_XFRM_TUNNEL
+ tristate
+ select INET6_TUNNEL
+ default n
+
config INET6_TUNNEL
- tristate "IPv6: tunnel transformation"
- depends on IPV6
- select XFRM
- ---help---
- Support for generic IPv6-in-IPv6 tunnel transformation, which is
- required by the IPv6-in-IPv6 tunneling module as well as tunnel mode
- IPComp.
-
- If unsure, say Y.
+ tristate
+ default n
config IPV6_TUNNEL
tristate "IPv6: IPv6-in-IPv6 tunnel"
+ select INET6_TUNNEL
depends on IPV6
---help---
Support for IPv6-in-IPv6 tunnels described in RFC 2473.
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 41877abd22e6..a760b0988fbb 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -18,7 +18,8 @@ ipv6-objs += $(ipv6-y)
obj-$(CONFIG_INET6_AH) += ah6.o
obj-$(CONFIG_INET6_ESP) += esp6.o
obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
-obj-$(CONFIG_INET6_TUNNEL) += xfrm6_tunnel.o
+obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o
+obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 48597538db3f..ff9040c92556 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -44,7 +44,6 @@
#include <net/ip.h>
#include <net/ipv6.h>
-#include <net/protocol.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/ip6_tunnel.h>
@@ -391,7 +390,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
* to the specifications in RFC 2473.
**/
-static void
+static int
ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
@@ -402,6 +401,7 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int rel_code = ICMPV6_ADDR_UNREACH;
__u32 rel_info = 0;
__u16 len;
+ int err = -ENOENT;
/* If the packet doesn't contain the original IPv6 header we are
in trouble since we might need the source address for further
@@ -411,6 +411,8 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL)
goto out;
+ err = 0;
+
switch (type) {
__u32 teli;
struct ipv6_tlv_tnl_enc_lim *tel;
@@ -492,6 +494,7 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
}
out:
read_unlock(&ip6ip6_lock);
+ return err;
}
static inline void ip6ip6_ecn_decapsulate(struct ipv6hdr *outer_iph,
@@ -511,9 +514,8 @@ static inline void ip6ip6_ecn_decapsulate(struct ipv6hdr *outer_iph,
**/
static int
-ip6ip6_rcv(struct sk_buff **pskb)
+ip6ip6_rcv(struct sk_buff *skb)
{
- struct sk_buff *skb = *pskb;
struct ipv6hdr *ipv6h;
struct ip6_tnl *t;
@@ -1112,39 +1114,12 @@ ip6ip6_fb_tnl_dev_init(struct net_device *dev)
return 0;
}
-#ifdef CONFIG_INET6_TUNNEL
static struct xfrm6_tunnel ip6ip6_handler = {
.handler = ip6ip6_rcv,
.err_handler = ip6ip6_err,
+ .priority = 1,
};
-static inline int ip6ip6_register(void)
-{
- return xfrm6_tunnel_register(&ip6ip6_handler);
-}
-
-static inline int ip6ip6_unregister(void)
-{
- return xfrm6_tunnel_deregister(&ip6ip6_handler);
-}
-#else
-static struct inet6_protocol xfrm6_tunnel_protocol = {
- .handler = ip6ip6_rcv,
- .err_handler = ip6ip6_err,
- .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
-};
-
-static inline int ip6ip6_register(void)
-{
- return inet6_add_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6);
-}
-
-static inline int ip6ip6_unregister(void)
-{
- return inet6_del_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6);
-}
-#endif
-
/**
* ip6_tunnel_init - register protocol and reserve needed resources
*
@@ -1155,7 +1130,7 @@ static int __init ip6_tunnel_init(void)
{
int err;
- if (ip6ip6_register() < 0) {
+ if (xfrm6_tunnel_register(&ip6ip6_handler)) {
printk(KERN_ERR "ip6ip6 init: can't register tunnel\n");
return -EAGAIN;
}
@@ -1174,7 +1149,7 @@ static int __init ip6_tunnel_init(void)
}
return 0;
fail:
- ip6ip6_unregister();
+ xfrm6_tunnel_deregister(&ip6ip6_handler);
return err;
}
@@ -1184,7 +1159,7 @@ fail:
static void __exit ip6_tunnel_cleanup(void)
{
- if (ip6ip6_unregister() < 0)
+ if (xfrm6_tunnel_deregister(&ip6ip6_handler))
printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n");
unregister_netdev(ip6ip6_fb_tnl_dev);
diff --git a/net/ipv6/tunnel6.c b/net/ipv6/tunnel6.c
new file mode 100644
index 000000000000..5659b52284bd
--- /dev/null
+++ b/net/ipv6/tunnel6.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C)2003,2004 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
+ *
+ * Authors Mitsuru KANDA <mk@linux-ipv6.org>
+ * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/xfrm.h>
+
+static struct xfrm6_tunnel *tunnel6_handlers;
+static DEFINE_MUTEX(tunnel6_mutex);
+
+int xfrm6_tunnel_register(struct xfrm6_tunnel *handler)
+{
+ struct xfrm6_tunnel **pprev;
+ int ret = -EEXIST;
+ int priority = handler->priority;
+
+ mutex_lock(&tunnel6_mutex);
+
+ for (pprev = &tunnel6_handlers; *pprev; pprev = &(*pprev)->next) {
+ if ((*pprev)->priority > priority)
+ break;
+ if ((*pprev)->priority == priority)
+ goto err;
+ }
+
+ handler->next = *pprev;
+ *pprev = handler;
+
+ ret = 0;
+
+err:
+ mutex_unlock(&tunnel6_mutex);
+
+ return ret;
+}
+
+EXPORT_SYMBOL(xfrm6_tunnel_register);
+
+int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler)
+{
+ struct xfrm6_tunnel **pprev;
+ int ret = -ENOENT;
+
+ mutex_lock(&tunnel6_mutex);
+
+ for (pprev = &tunnel6_handlers; *pprev; pprev = &(*pprev)->next) {
+ if (*pprev == handler) {
+ *pprev = handler->next;
+ ret = 0;
+ break;
+ }
+ }
+
+ mutex_unlock(&tunnel6_mutex);
+
+ synchronize_net();
+
+ return ret;
+}
+
+EXPORT_SYMBOL(xfrm6_tunnel_deregister);
+
+static int tunnel6_rcv(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct xfrm6_tunnel *handler;
+
+ for (handler = tunnel6_handlers; handler; handler = handler->next)
+ if (!handler->handler(skb))
+ return 0;
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static void tunnel6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct xfrm6_tunnel *handler;
+
+ for (handler = tunnel6_handlers; handler; handler = handler->next)
+ if (!handler->err_handler(skb, opt, type, code, offset, info))
+ break;
+}
+
+static struct inet6_protocol tunnel6_protocol = {
+ .handler = tunnel6_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;
+ }
+ return 0;
+}
+
+static void __exit tunnel6_fini(void)
+{
+ if (inet6_del_protocol(&tunnel6_protocol, IPPROTO_IPV6))
+ printk(KERN_ERR "tunnel6 close: can't remove protocol\n");
+}
+
+module_init(tunnel6_init);
+module_exit(tunnel6_fini);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
index 1ca2da68ef69..cccf8b76f046 100644
--- a/net/ipv6/xfrm6_input.c
+++ b/net/ipv6/xfrm6_input.c
@@ -28,9 +28,8 @@ static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
IP6_ECN_set_ce(inner_iph);
}
-int xfrm6_rcv_spi(struct sk_buff **pskb, u32 spi)
+int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
{
- struct sk_buff *skb = *pskb;
int err;
u32 seq;
struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
@@ -159,5 +158,5 @@ EXPORT_SYMBOL(xfrm6_rcv_spi);
int xfrm6_rcv(struct sk_buff **pskb)
{
- return xfrm6_rcv_spi(pskb, 0);
+ return xfrm6_rcv_spi(*pskb, 0);
}
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
index 08f9abbdf1d7..a8f6776c518d 100644
--- a/net/ipv6/xfrm6_tunnel.c
+++ b/net/ipv6/xfrm6_tunnel.c
@@ -28,7 +28,6 @@
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/ipv6.h>
-#include <net/protocol.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/mutex.h>
@@ -357,71 +356,18 @@ static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *dec
return 0;
}
-static struct xfrm6_tunnel *xfrm6_tunnel_handler;
-static DEFINE_MUTEX(xfrm6_tunnel_mutex);
-
-int xfrm6_tunnel_register(struct xfrm6_tunnel *handler)
+static int xfrm6_tunnel_rcv(struct sk_buff *skb)
{
- int ret;
-
- mutex_lock(&xfrm6_tunnel_mutex);
- ret = 0;
- if (xfrm6_tunnel_handler != NULL)
- ret = -EINVAL;
- if (!ret)
- xfrm6_tunnel_handler = handler;
- mutex_unlock(&xfrm6_tunnel_mutex);
-
- return ret;
-}
-
-EXPORT_SYMBOL(xfrm6_tunnel_register);
-
-int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler)
-{
- int ret;
-
- mutex_lock(&xfrm6_tunnel_mutex);
- ret = 0;
- if (xfrm6_tunnel_handler != handler)
- ret = -EINVAL;
- if (!ret)
- xfrm6_tunnel_handler = NULL;
- mutex_unlock(&xfrm6_tunnel_mutex);
-
- synchronize_net();
-
- return ret;
-}
-
-EXPORT_SYMBOL(xfrm6_tunnel_deregister);
-
-static int xfrm6_tunnel_rcv(struct sk_buff **pskb)
-{
- struct sk_buff *skb = *pskb;
- struct xfrm6_tunnel *handler = xfrm6_tunnel_handler;
struct ipv6hdr *iph = skb->nh.ipv6h;
u32 spi;
- /* device-like_ip6ip6_handler() */
- if (handler && handler->handler(pskb) == 0)
- return 0;
-
spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr);
- return xfrm6_rcv_spi(pskb, spi);
+ return xfrm6_rcv_spi(skb, spi);
}
-static void xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- int type, int code, int offset, __u32 info)
+static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
{
- struct xfrm6_tunnel *handler = xfrm6_tunnel_handler;
-
- /* call here first for device-like ip6ip6 err handling */
- if (handler) {
- handler->err_handler(skb, opt, type, code, offset, info);
- return;
- }
-
/* xfrm6_tunnel native err handling */
switch (type) {
case ICMPV6_DEST_UNREACH:
@@ -462,7 +408,8 @@ static void xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
default:
break;
}
- return;
+
+ return 0;
}
static int xfrm6_tunnel_init_state(struct xfrm_state *x)
@@ -493,10 +440,10 @@ static struct xfrm_type xfrm6_tunnel_type = {
.output = xfrm6_tunnel_output,
};
-static struct inet6_protocol xfrm6_tunnel_protocol = {
+static struct xfrm6_tunnel xfrm6_tunnel_handler = {
.handler = xfrm6_tunnel_rcv,
- .err_handler = xfrm6_tunnel_err,
- .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
+ .err_handler = xfrm6_tunnel_err,
+ .priority = 2,
};
static int __init xfrm6_tunnel_init(void)
@@ -508,16 +455,16 @@ static int __init xfrm6_tunnel_init(void)
"xfrm6_tunnel init: can't add xfrm type\n");
return -EAGAIN;
}
- if (inet6_add_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6) < 0) {
+ if (xfrm6_tunnel_register(&xfrm6_tunnel_handler)) {
X6TPRINTK1(KERN_ERR
- "xfrm6_tunnel init(): can't add protocol\n");
+ "xfrm6_tunnel init(): can't add handler\n");
xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
return -EAGAIN;
}
if (xfrm6_tunnel_spi_init() < 0) {
X6TPRINTK1(KERN_ERR
"xfrm6_tunnel init: failed to initialize spi\n");
- inet6_del_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6);
+ xfrm6_tunnel_deregister(&xfrm6_tunnel_handler);
xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6);
return -EAGAIN;
}
@@ -529,9 +476,9 @@ static void __exit xfrm6_tunnel_fini(void)
X6TPRINTK3(KERN_DEBUG "%s()\n", __FUNCTION__);
xfrm6_tunnel_spi_fini();
- if (inet6_del_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6) < 0)
+ if (xfrm6_tunnel_deregister(&xfrm6_tunnel_handler))
X6TPRINTK1(KERN_ERR
- "xfrm6_tunnel close: can't remove protocol\n");
+ "xfrm6_tunnel close: can't remove handler\n");
if (xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6) < 0)
X6TPRINTK1(KERN_ERR
"xfrm6_tunnel close: can't remove xfrm type\n");