1d2acc347SHerbert Xu /* tunnel4.c: Generic IP tunnel transformer. 2d2acc347SHerbert Xu * 3d2acc347SHerbert Xu * Copyright (C) 2003 David S. Miller (davem@redhat.com) 4d2acc347SHerbert Xu */ 5d2acc347SHerbert Xu 6d2acc347SHerbert Xu #include <linux/init.h> 7d2acc347SHerbert Xu #include <linux/module.h> 8d2acc347SHerbert Xu #include <linux/mutex.h> 9*8afe97e5SSimon Horman #include <linux/mpls.h> 10d2acc347SHerbert Xu #include <linux/netdevice.h> 11d2acc347SHerbert Xu #include <linux/skbuff.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 1350fba2aaSHerbert Xu #include <net/icmp.h> 1450fba2aaSHerbert Xu #include <net/ip.h> 15d2acc347SHerbert Xu #include <net/protocol.h> 16d2acc347SHerbert Xu #include <net/xfrm.h> 17d2acc347SHerbert Xu 18b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly; 19b33eab08SEric Dumazet static struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly; 20*8afe97e5SSimon Horman static struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly; 21d2acc347SHerbert Xu static DEFINE_MUTEX(tunnel4_mutex); 22d2acc347SHerbert Xu 23b33eab08SEric Dumazet static inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family) 24358352b8SPavel Emelyanov { 25*8afe97e5SSimon Horman return (family == AF_INET) ? &tunnel4_handlers : 26*8afe97e5SSimon Horman (family == AF_INET6) ? &tunnel64_handlers : 27*8afe97e5SSimon Horman &tunnelmpls4_handlers; 28358352b8SPavel Emelyanov } 29358352b8SPavel Emelyanov 30c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family) 31d2acc347SHerbert Xu { 32b33eab08SEric Dumazet struct xfrm_tunnel __rcu **pprev; 33b33eab08SEric Dumazet struct xfrm_tunnel *t; 34b33eab08SEric Dumazet 35d2acc347SHerbert Xu int ret = -EEXIST; 36d2acc347SHerbert Xu int priority = handler->priority; 37d2acc347SHerbert Xu 38d2acc347SHerbert Xu mutex_lock(&tunnel4_mutex); 39d2acc347SHerbert Xu 40b33eab08SEric Dumazet for (pprev = fam_handlers(family); 41b33eab08SEric Dumazet (t = rcu_dereference_protected(*pprev, 42b33eab08SEric Dumazet lockdep_is_held(&tunnel4_mutex))) != NULL; 43b33eab08SEric Dumazet pprev = &t->next) { 44b33eab08SEric Dumazet if (t->priority > priority) 45d2acc347SHerbert Xu break; 46b33eab08SEric Dumazet if (t->priority == priority) 47d2acc347SHerbert Xu goto err; 48d2acc347SHerbert Xu } 49d2acc347SHerbert Xu 50d2acc347SHerbert Xu handler->next = *pprev; 5149d61e23SEric Dumazet rcu_assign_pointer(*pprev, handler); 52d2acc347SHerbert Xu 53d2acc347SHerbert Xu ret = 0; 54d2acc347SHerbert Xu 55d2acc347SHerbert Xu err: 56d2acc347SHerbert Xu mutex_unlock(&tunnel4_mutex); 57d2acc347SHerbert Xu 58d2acc347SHerbert Xu return ret; 59d2acc347SHerbert Xu } 60d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_register); 61d2acc347SHerbert Xu 62c0d56408SKazunori MIYAZAWA int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family) 63d2acc347SHerbert Xu { 64b33eab08SEric Dumazet struct xfrm_tunnel __rcu **pprev; 65b33eab08SEric Dumazet struct xfrm_tunnel *t; 66d2acc347SHerbert Xu int ret = -ENOENT; 67d2acc347SHerbert Xu 68d2acc347SHerbert Xu mutex_lock(&tunnel4_mutex); 69d2acc347SHerbert Xu 70b33eab08SEric Dumazet for (pprev = fam_handlers(family); 71b33eab08SEric Dumazet (t = rcu_dereference_protected(*pprev, 72b33eab08SEric Dumazet lockdep_is_held(&tunnel4_mutex))) != NULL; 73b33eab08SEric Dumazet pprev = &t->next) { 74b33eab08SEric Dumazet if (t == handler) { 75d2acc347SHerbert Xu *pprev = handler->next; 76d2acc347SHerbert Xu ret = 0; 77d2acc347SHerbert Xu break; 78d2acc347SHerbert Xu } 79d2acc347SHerbert Xu } 80d2acc347SHerbert Xu 81d2acc347SHerbert Xu mutex_unlock(&tunnel4_mutex); 82d2acc347SHerbert Xu 83d2acc347SHerbert Xu synchronize_net(); 84d2acc347SHerbert Xu 85d2acc347SHerbert Xu return ret; 86d2acc347SHerbert Xu } 87d2acc347SHerbert Xu EXPORT_SYMBOL(xfrm4_tunnel_deregister); 88d2acc347SHerbert Xu 89875168a9SEric Dumazet #define for_each_tunnel_rcu(head, handler) \ 90875168a9SEric Dumazet for (handler = rcu_dereference(head); \ 91875168a9SEric Dumazet handler != NULL; \ 92875168a9SEric Dumazet handler = rcu_dereference(handler->next)) \ 93875168a9SEric Dumazet 94d2acc347SHerbert Xu static int tunnel4_rcv(struct sk_buff *skb) 95d2acc347SHerbert Xu { 96d2acc347SHerbert Xu struct xfrm_tunnel *handler; 97d2acc347SHerbert Xu 9850fba2aaSHerbert Xu if (!pskb_may_pull(skb, sizeof(struct iphdr))) 9950fba2aaSHerbert Xu goto drop; 10050fba2aaSHerbert Xu 101875168a9SEric Dumazet for_each_tunnel_rcu(tunnel4_handlers, handler) 102d2acc347SHerbert Xu if (!handler->handler(skb)) 103d2acc347SHerbert Xu return 0; 104d2acc347SHerbert Xu 10550fba2aaSHerbert Xu icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); 10650fba2aaSHerbert Xu 10750fba2aaSHerbert Xu drop: 108d2acc347SHerbert Xu kfree_skb(skb); 109d2acc347SHerbert Xu return 0; 110d2acc347SHerbert Xu } 111d2acc347SHerbert Xu 112dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 113c0d56408SKazunori MIYAZAWA static int tunnel64_rcv(struct sk_buff *skb) 114c0d56408SKazunori MIYAZAWA { 115c0d56408SKazunori MIYAZAWA struct xfrm_tunnel *handler; 116c0d56408SKazunori MIYAZAWA 117baa2bfb8SYOSHIFUJI Hideaki if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) 118c0d56408SKazunori MIYAZAWA goto drop; 119c0d56408SKazunori MIYAZAWA 120875168a9SEric Dumazet for_each_tunnel_rcu(tunnel64_handlers, handler) 121c0d56408SKazunori MIYAZAWA if (!handler->handler(skb)) 122c0d56408SKazunori MIYAZAWA return 0; 123c0d56408SKazunori MIYAZAWA 124c0d56408SKazunori MIYAZAWA icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); 125c0d56408SKazunori MIYAZAWA 126c0d56408SKazunori MIYAZAWA drop: 127c0d56408SKazunori MIYAZAWA kfree_skb(skb); 128c0d56408SKazunori MIYAZAWA return 0; 129c0d56408SKazunori MIYAZAWA } 130c0d56408SKazunori MIYAZAWA #endif 131c0d56408SKazunori MIYAZAWA 132*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 133*8afe97e5SSimon Horman static int tunnelmpls4_rcv(struct sk_buff *skb) 134*8afe97e5SSimon Horman { 135*8afe97e5SSimon Horman struct xfrm_tunnel *handler; 136*8afe97e5SSimon Horman 137*8afe97e5SSimon Horman if (!pskb_may_pull(skb, sizeof(struct mpls_label))) 138*8afe97e5SSimon Horman goto drop; 139*8afe97e5SSimon Horman 140*8afe97e5SSimon Horman for_each_tunnel_rcu(tunnelmpls4_handlers, handler) 141*8afe97e5SSimon Horman if (!handler->handler(skb)) 142*8afe97e5SSimon Horman return 0; 143*8afe97e5SSimon Horman 144*8afe97e5SSimon Horman icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); 145*8afe97e5SSimon Horman 146*8afe97e5SSimon Horman drop: 147*8afe97e5SSimon Horman kfree_skb(skb); 148*8afe97e5SSimon Horman return 0; 149*8afe97e5SSimon Horman } 150*8afe97e5SSimon Horman #endif 151*8afe97e5SSimon Horman 152d2acc347SHerbert Xu static void tunnel4_err(struct sk_buff *skb, u32 info) 153d2acc347SHerbert Xu { 154d2acc347SHerbert Xu struct xfrm_tunnel *handler; 155d2acc347SHerbert Xu 156875168a9SEric Dumazet for_each_tunnel_rcu(tunnel4_handlers, handler) 157d2acc347SHerbert Xu if (!handler->err_handler(skb, info)) 158d2acc347SHerbert Xu break; 159d2acc347SHerbert Xu } 160d2acc347SHerbert Xu 161dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 16299f93326SPavel Emelyanov static void tunnel64_err(struct sk_buff *skb, u32 info) 16399f93326SPavel Emelyanov { 16499f93326SPavel Emelyanov struct xfrm_tunnel *handler; 16599f93326SPavel Emelyanov 166875168a9SEric Dumazet for_each_tunnel_rcu(tunnel64_handlers, handler) 16799f93326SPavel Emelyanov if (!handler->err_handler(skb, info)) 16899f93326SPavel Emelyanov break; 16999f93326SPavel Emelyanov } 17099f93326SPavel Emelyanov #endif 17199f93326SPavel Emelyanov 172*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 173*8afe97e5SSimon Horman static void tunnelmpls4_err(struct sk_buff *skb, u32 info) 174*8afe97e5SSimon Horman { 175*8afe97e5SSimon Horman struct xfrm_tunnel *handler; 176*8afe97e5SSimon Horman 177*8afe97e5SSimon Horman for_each_tunnel_rcu(tunnelmpls4_handlers, handler) 178*8afe97e5SSimon Horman if (!handler->err_handler(skb, info)) 179*8afe97e5SSimon Horman break; 180*8afe97e5SSimon Horman } 181*8afe97e5SSimon Horman #endif 182*8afe97e5SSimon Horman 18332613090SAlexey Dobriyan static const struct net_protocol tunnel4_protocol = { 184d2acc347SHerbert Xu .handler = tunnel4_rcv, 185d2acc347SHerbert Xu .err_handler = tunnel4_err, 186d2acc347SHerbert Xu .no_policy = 1, 1874597a0ceSPavel Emelyanov .netns_ok = 1, 188d2acc347SHerbert Xu }; 189d2acc347SHerbert Xu 190dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 19132613090SAlexey Dobriyan static const struct net_protocol tunnel64_protocol = { 192c0d56408SKazunori MIYAZAWA .handler = tunnel64_rcv, 19399f93326SPavel Emelyanov .err_handler = tunnel64_err, 194c0d56408SKazunori MIYAZAWA .no_policy = 1, 195b0970c42SPavel Emelyanov .netns_ok = 1, 196c0d56408SKazunori MIYAZAWA }; 197c0d56408SKazunori MIYAZAWA #endif 198c0d56408SKazunori MIYAZAWA 199*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 200*8afe97e5SSimon Horman static const struct net_protocol tunnelmpls4_protocol = { 201*8afe97e5SSimon Horman .handler = tunnelmpls4_rcv, 202*8afe97e5SSimon Horman .err_handler = tunnelmpls4_err, 203*8afe97e5SSimon Horman .no_policy = 1, 204*8afe97e5SSimon Horman .netns_ok = 1, 205*8afe97e5SSimon Horman }; 206*8afe97e5SSimon Horman #endif 207*8afe97e5SSimon Horman 208d2acc347SHerbert Xu static int __init tunnel4_init(void) 209d2acc347SHerbert Xu { 210*8afe97e5SSimon Horman if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) 211*8afe97e5SSimon Horman goto err_ipip; 212dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 213*8afe97e5SSimon Horman if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) 214*8afe97e5SSimon Horman goto err_ipv6; 215*8afe97e5SSimon Horman #endif 216*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 217*8afe97e5SSimon Horman if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) 218*8afe97e5SSimon Horman goto err_mpls; 219c0d56408SKazunori MIYAZAWA #endif 220d2acc347SHerbert Xu return 0; 221*8afe97e5SSimon Horman 222*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_IPV6) 223*8afe97e5SSimon Horman err_mpls: 224*8afe97e5SSimon Horman inet_del_protocol(&tunnel4_protocol, IPPROTO_IPV6); 225*8afe97e5SSimon Horman #endif 226*8afe97e5SSimon Horman err_ipv6: 227*8afe97e5SSimon Horman inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); 228*8afe97e5SSimon Horman err_ipip: 229*8afe97e5SSimon Horman pr_err("%s: can't add protocol\n", __func__); 230*8afe97e5SSimon Horman return -EAGAIN; 231d2acc347SHerbert Xu } 232d2acc347SHerbert Xu 233d2acc347SHerbert Xu static void __exit tunnel4_fini(void) 234d2acc347SHerbert Xu { 235*8afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 236*8afe97e5SSimon Horman if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) 237*8afe97e5SSimon Horman pr_err("tunnelmpls4 close: can't remove protocol\n"); 238*8afe97e5SSimon Horman #endif 239dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 240c0d56408SKazunori MIYAZAWA if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6)) 241058bd4d2SJoe Perches pr_err("tunnel64 close: can't remove protocol\n"); 242c0d56408SKazunori MIYAZAWA #endif 243d2acc347SHerbert Xu if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP)) 244058bd4d2SJoe Perches pr_err("tunnel4 close: can't remove protocol\n"); 245d2acc347SHerbert Xu } 246d2acc347SHerbert Xu 247d2acc347SHerbert Xu module_init(tunnel4_init); 248d2acc347SHerbert Xu module_exit(tunnel4_fini); 249d2acc347SHerbert Xu MODULE_LICENSE("GPL"); 250