Commit 66d2856c authored by David S. Miller's avatar David S. Miller

[IPSEC]: Add ipv4 tunnel transformer.

parent 925511b7
......@@ -723,6 +723,12 @@ struct xfrm_algo_desc {
struct sadb_alg desc;
};
/* XFRM tunnel handlers. */
struct xfrm_tunnel {
int (*handler)(struct sk_buff *skb);
void (*err_handler)(struct sk_buff *skb, void *info);
};
extern void xfrm_init(void);
extern void xfrm4_init(void);
extern void xfrm4_fini(void);
......@@ -752,6 +758,8 @@ extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq);
extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl);
extern int xfrm4_rcv(struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type);
extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler);
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler);
extern int xfrm6_rcv(struct sk_buff **pskb);
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);
......
......@@ -22,4 +22,4 @@ obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-y += xfrm4_policy.o xfrm4_state.o xfrm4_input.o
obj-y += xfrm4_policy.o xfrm4_state.o xfrm4_input.o xfrm4_tunnel.o
......@@ -115,6 +115,7 @@
#include <net/protocol.h>
#include <net/ipip.h>
#include <net/inet_ecn.h>
#include <net/xfrm.h>
#define HASH_SIZE 16
#define HASH(addr) ((addr^(addr>>4))&0xF)
......@@ -285,7 +286,7 @@ static void ipip_tunnel_uninit(struct net_device *dev)
dev_put(dev);
}
void ipip_err(struct sk_buff *skb, u32 info)
void ipip_err(struct sk_buff *skb, void *__unused)
{
#ifndef I_WISH_WORLD_WERE_PERFECT
......@@ -468,11 +469,13 @@ void ipip_err(struct sk_buff *skb, u32 info)
#endif
}
static inline void ipip_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb)
{
if (INET_ECN_is_ce(iph->tos) &&
INET_ECN_is_not_ce(skb->nh.iph->tos))
IP_ECN_set_ce(iph);
struct iphdr *inner_iph = skb->nh.iph;
if (INET_ECN_is_ce(outer_iph->tos) &&
INET_ECN_is_not_ce(inner_iph->tos))
IP_ECN_set_ce(inner_iph);
}
int ipip_rcv(struct sk_buff *skb)
......@@ -511,10 +514,8 @@ int ipip_rcv(struct sk_buff *skb)
}
read_unlock(&ipip_lock);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
out:
kfree_skb(skb);
return 0;
return -1;
}
/*
......@@ -867,7 +868,7 @@ int __init ipip_fb_tunnel_init(struct net_device *dev)
return 0;
}
static struct inet_protocol ipip_protocol = {
static struct xfrm_tunnel ipip_handler = {
.handler = ipip_rcv,
.err_handler = ipip_err,
};
......@@ -879,8 +880,8 @@ int __init ipip_init(void)
{
printk(banner);
if (inet_add_protocol(&ipip_protocol, IPPROTO_IPIP) < 0) {
printk(KERN_INFO "ipip init: can't add protocol\n");
if (xfrm4_tunnel_register(&ipip_handler) < 0) {
printk(KERN_INFO "ipip init: can't register tunnel\n");
return -EAGAIN;
}
......@@ -892,8 +893,8 @@ 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(&ipip_handler) < 0)
printk(KERN_INFO "ipip close: can't deregister tunnel\n");
unregister_netdev(&ipip_fb_tunnel_dev);
}
......
/* xfrm4_tunnel.c: Generic IP tunnel transformer.
*
* Copyright (C) 2003 David S. Miller (davem@redhat.com)
*/
#include <linux/skbuff.h>
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/inet_ecn.h>
static int ipip_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct iphdr *iph, *top_iph;
int tos;
iph = skb->nh.iph;
spin_lock_bh(&x->lock);
tos = iph->tos;
top_iph = (struct iphdr *) skb_push(skb, x->props.header_len);
top_iph->ihl = 5;
top_iph->version = 4;
top_iph->tos = INET_ECN_encapsulate(tos, iph->tos);
top_iph->tot_len = htons(skb->len);
top_iph->frag_off = iph->frag_off & ~htons(IP_MF|IP_OFFSET);
if (!(iph->frag_off & htons(IP_DF)))
__ip_select_ident(top_iph, dst, 0);
top_iph->ttl = iph->ttl;
top_iph->protocol = IPPROTO_IPIP;
top_iph->check = 0;
top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
ip_send_check(top_iph);
skb->nh.raw = skb->data;
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock_bh(&x->lock);
if ((skb->dst = dst_pop(dst)) == NULL) {
kfree_skb(skb);
return -EHOSTUNREACH;
}
return NET_XMIT_BYPASS;
}
static inline void ipip_ecn_decapsulate(struct iphdr *outer_iph, struct sk_buff *skb)
{
struct iphdr *inner_iph = skb->nh.iph;
if (INET_ECN_is_ce(outer_iph->tos) &&
INET_ECN_is_not_ce(inner_iph->tos))
IP_ECN_set_ce(inner_iph);
}
static int ipip_xfrm_rcv(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
{
struct iphdr *outer_iph = skb->nh.iph;
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
return -EINVAL;
skb->mac.raw = skb->nh.raw;
skb->nh.raw = skb->data;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
dst_release(skb->dst);
skb->dst = NULL;
skb->protocol = htons(ETH_P_IP);
skb->pkt_type = PACKET_HOST;
ipip_ecn_decapsulate(outer_iph, skb);
netif_rx(skb);
return 0;
}
static struct xfrm_tunnel *ipip_handler;
static DECLARE_MUTEX(xfrm4_tunnel_sem);
int xfrm4_tunnel_register(struct xfrm_tunnel *handler)
{
int ret;
down(&xfrm4_tunnel_sem);
ret = 0;
if (ipip_handler != NULL)
ret = -EINVAL;
if (!ret)
ipip_handler = handler;
up(&xfrm4_tunnel_sem);
return ret;
}
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler)
{
int ret;
down(&xfrm4_tunnel_sem);
ret = 0;
if (ipip_handler != handler)
ret = -EINVAL;
if (!ret)
ipip_handler = NULL;
up(&xfrm4_tunnel_sem);
synchronize_net();
return ret;
}
static int ipip_rcv(struct sk_buff *skb)
{
struct xfrm_tunnel *handler = ipip_handler;
struct xfrm_state *x = NULL;
int err;
/* Tunnel devices take precedence. */
if (handler) {
err = handler->handler(skb);
if (!err)
goto out;
}
x = xfrm_state_lookup((xfrm_address_t *)&skb->nh.iph->daddr,
skb->nh.iph->saddr,
IPPROTO_IPIP, AF_INET);
if (x) {
spin_lock(&x->lock);
if (unlikely(x->km.state != XFRM_STATE_VALID))
goto drop_unlock;
}
err = ipip_xfrm_rcv(x, NULL, skb);
if (err)
goto drop_unlock;
if (x) {
x->curlft.bytes += skb->len;
x->curlft.packets++;
spin_unlock(&x->lock);
xfrm_state_put(x);
}
return 0;
drop_unlock:
if (x) {
spin_unlock(&x->lock);
xfrm_state_put(x);
}
kfree_skb(skb);
out:
return 0;
}
void ipip_err(struct sk_buff *skb, u32 info)
{
struct xfrm_tunnel *handler = ipip_handler;
u32 arg = info;
if (handler)
handler->err_handler(skb, &arg);
}
static int ipip_init_state(struct xfrm_state *x, void *args)
{
x->props.header_len = sizeof(struct iphdr);
return 0;
}
static void ipip_destroy(struct xfrm_state *x)
{
}
static struct xfrm_type ipip_type = {
.description = "IPIP",
.proto = IPPROTO_IPIP,
.init_state = ipip_init_state,
.destructor = ipip_destroy,
.input = ipip_xfrm_rcv,
.output = ipip_output
};
static struct inet_protocol ipip_protocol = {
.handler = ipip_rcv,
.err_handler = ipip_err,
};
static int __init ipip_init(void)
{
SET_MODULE_OWNER(&ipip_type);
if (xfrm_register_type(&ipip_type, AF_INET) < 0) {
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");
xfrm_unregister_type(&ipip_type, AF_INET);
return -EAGAIN;
}
return 0;
}
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 (xfrm_unregister_type(&ipip_type, AF_INET) < 0)
printk(KERN_INFO "ipip close: can't remove xfrm type\n");
}
module_init(ipip_init);
module_exit(ipip_fini);
MODULE_LICENSE("GPL");
......@@ -318,6 +318,8 @@ EXPORT_SYMBOL(__secpath_destroy);
EXPORT_SYMBOL(xfrm_get_acqseq);
EXPORT_SYMBOL(xfrm_parse_spi);
EXPORT_SYMBOL(xfrm4_rcv);
EXPORT_SYMBOL(xfrm4_tunnel_register);
EXPORT_SYMBOL(xfrm4_tunnel_deregister);
EXPORT_SYMBOL(xfrm_register_type);
EXPORT_SYMBOL(xfrm_unregister_type);
EXPORT_SYMBOL(xfrm_get_type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment