From 66d2856ca9861bcea0a9120a7dda25b81080dc23 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 11 Apr 2003 12:15:54 -0700 Subject: [PATCH] [IPSEC]: Add ipv4 tunnel transformer. --- include/net/xfrm.h | 8 ++ net/ipv4/Makefile | 2 +- net/ipv4/ipip.c | 27 ++--- net/ipv4/xfrm4_tunnel.c | 225 ++++++++++++++++++++++++++++++++++++++++ net/netsyms.c | 2 + 5 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 net/ipv4/xfrm4_tunnel.c diff --git a/include/net/xfrm.h b/include/net/xfrm.h index b75d2651adf06..be3c652da8c7c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -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); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 4a7df22b26fc3..c9b1412cda697 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -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 diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index e948f33680533..28c17a319eaed 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -115,6 +115,7 @@ #include #include #include +#include #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 @@ out: #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); } diff --git a/net/ipv4/xfrm4_tunnel.c b/net/ipv4/xfrm4_tunnel.c new file mode 100644 index 0000000000000..c4fe071b56b52 --- /dev/null +++ b/net/ipv4/xfrm4_tunnel.c @@ -0,0 +1,225 @@ +/* xfrm4_tunnel.c: Generic IP tunnel transformer. + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + */ + +#include +#include +#include +#include +#include + +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"); diff --git a/net/netsyms.c b/net/netsyms.c index b55b5fed2c81d..d52085580dd3c 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -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); -- 2.53.0