forked from luck/tmp_suning_uos_patched
50fba2aa7c
This patch moves the sending of ICMP messages when there are no IPv4/IPv6 tunnels present to tunnel4/tunnel6 respectively. Please note that for now if xfrm4_tunnel/xfrm6_tunnel is loaded then no ICMP messages will ever be sent. This is similar to how we handle AH/ESP/IPCOMP. This move fixes the bug where we always send an ICMP message when there is no ip6_tunnel device present for a given packet even if it is later handled by IPsec. It also causes ICMP messages to be sent when no IPIP tunnel is present. I've decided to use the "port unreachable" ICMP message over the current value of "address unreachable" (and "protocol unreachable" by GRE) because it is not ambiguous unlike the other ones which can be triggered by other conditions. There seems to be no standard specifying what value must be used so this change should be OK. In fact we should change GRE to use this value as well. Incidentally, this patch also fixes a fairly serious bug in xfrm6_tunnel where we don't check whether the embedded IPv6 header is present before dereferencing it for the inside source address. This patch is inspired by a previous patch by Hugo Santos <hsantos@av.it.pt>. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
122 lines
2.4 KiB
C
122 lines
2.4 KiB
C
/* 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/icmp.h>
|
|
#include <net/ip.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;
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
|
goto drop;
|
|
|
|
for (handler = tunnel4_handlers; handler; handler = handler->next)
|
|
if (!handler->handler(skb))
|
|
return 0;
|
|
|
|
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
|
|
|
|
drop:
|
|
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");
|