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> 98afe97e5SSimon 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; 208afe97e5SSimon 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 { 258afe97e5SSimon Horman return (family == AF_INET) ? &tunnel4_handlers : 268afe97e5SSimon Horman (family == AF_INET6) ? &tunnel64_handlers : 278afe97e5SSimon 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 1328afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 1338afe97e5SSimon Horman static int tunnelmpls4_rcv(struct sk_buff *skb) 1348afe97e5SSimon Horman { 1358afe97e5SSimon Horman struct xfrm_tunnel *handler; 1368afe97e5SSimon Horman 1378afe97e5SSimon Horman if (!pskb_may_pull(skb, sizeof(struct mpls_label))) 1388afe97e5SSimon Horman goto drop; 1398afe97e5SSimon Horman 1408afe97e5SSimon Horman for_each_tunnel_rcu(tunnelmpls4_handlers, handler) 1418afe97e5SSimon Horman if (!handler->handler(skb)) 1428afe97e5SSimon Horman return 0; 1438afe97e5SSimon Horman 1448afe97e5SSimon Horman icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); 1458afe97e5SSimon Horman 1468afe97e5SSimon Horman drop: 1478afe97e5SSimon Horman kfree_skb(skb); 1488afe97e5SSimon Horman return 0; 1498afe97e5SSimon Horman } 1508afe97e5SSimon Horman #endif 1518afe97e5SSimon Horman 152*32bbd879SStefano Brivio static int 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)) 158*32bbd879SStefano Brivio return 0; 159*32bbd879SStefano Brivio 160*32bbd879SStefano Brivio return -ENOENT; 161d2acc347SHerbert Xu } 162d2acc347SHerbert Xu 163dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 164*32bbd879SStefano Brivio static int tunnel64_err(struct sk_buff *skb, u32 info) 16599f93326SPavel Emelyanov { 16699f93326SPavel Emelyanov struct xfrm_tunnel *handler; 16799f93326SPavel Emelyanov 168875168a9SEric Dumazet for_each_tunnel_rcu(tunnel64_handlers, handler) 16999f93326SPavel Emelyanov if (!handler->err_handler(skb, info)) 170*32bbd879SStefano Brivio return 0; 171*32bbd879SStefano Brivio 172*32bbd879SStefano Brivio return -ENOENT; 17399f93326SPavel Emelyanov } 17499f93326SPavel Emelyanov #endif 17599f93326SPavel Emelyanov 1768afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 177*32bbd879SStefano Brivio static int tunnelmpls4_err(struct sk_buff *skb, u32 info) 1788afe97e5SSimon Horman { 1798afe97e5SSimon Horman struct xfrm_tunnel *handler; 1808afe97e5SSimon Horman 1818afe97e5SSimon Horman for_each_tunnel_rcu(tunnelmpls4_handlers, handler) 1828afe97e5SSimon Horman if (!handler->err_handler(skb, info)) 183*32bbd879SStefano Brivio return 0; 184*32bbd879SStefano Brivio 185*32bbd879SStefano Brivio return -ENOENT; 1868afe97e5SSimon Horman } 1878afe97e5SSimon Horman #endif 1888afe97e5SSimon Horman 18932613090SAlexey Dobriyan static const struct net_protocol tunnel4_protocol = { 190d2acc347SHerbert Xu .handler = tunnel4_rcv, 191d2acc347SHerbert Xu .err_handler = tunnel4_err, 192d2acc347SHerbert Xu .no_policy = 1, 1934597a0ceSPavel Emelyanov .netns_ok = 1, 194d2acc347SHerbert Xu }; 195d2acc347SHerbert Xu 196dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 19732613090SAlexey Dobriyan static const struct net_protocol tunnel64_protocol = { 198c0d56408SKazunori MIYAZAWA .handler = tunnel64_rcv, 19999f93326SPavel Emelyanov .err_handler = tunnel64_err, 200c0d56408SKazunori MIYAZAWA .no_policy = 1, 201b0970c42SPavel Emelyanov .netns_ok = 1, 202c0d56408SKazunori MIYAZAWA }; 203c0d56408SKazunori MIYAZAWA #endif 204c0d56408SKazunori MIYAZAWA 2058afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 2068afe97e5SSimon Horman static const struct net_protocol tunnelmpls4_protocol = { 2078afe97e5SSimon Horman .handler = tunnelmpls4_rcv, 2088afe97e5SSimon Horman .err_handler = tunnelmpls4_err, 2098afe97e5SSimon Horman .no_policy = 1, 2108afe97e5SSimon Horman .netns_ok = 1, 2118afe97e5SSimon Horman }; 2128afe97e5SSimon Horman #endif 2138afe97e5SSimon Horman 214d2acc347SHerbert Xu static int __init tunnel4_init(void) 215d2acc347SHerbert Xu { 2168afe97e5SSimon Horman if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP)) 217aa9667e7SSimon Horman goto err; 218dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 219aa9667e7SSimon Horman if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) { 220aa9667e7SSimon Horman inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); 221aa9667e7SSimon Horman goto err; 222aa9667e7SSimon Horman } 2238afe97e5SSimon Horman #endif 2248afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 225aa9667e7SSimon Horman if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) { 226aa9667e7SSimon Horman inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP); 227aa9667e7SSimon Horman #if IS_ENABLED(CONFIG_IPV6) 228aa9667e7SSimon Horman inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6); 229aa9667e7SSimon Horman #endif 230aa9667e7SSimon Horman goto err; 231aa9667e7SSimon Horman } 232c0d56408SKazunori MIYAZAWA #endif 233d2acc347SHerbert Xu return 0; 2348afe97e5SSimon Horman 235aa9667e7SSimon Horman err: 2368afe97e5SSimon Horman pr_err("%s: can't add protocol\n", __func__); 2378afe97e5SSimon Horman return -EAGAIN; 238d2acc347SHerbert Xu } 239d2acc347SHerbert Xu 240d2acc347SHerbert Xu static void __exit tunnel4_fini(void) 241d2acc347SHerbert Xu { 2428afe97e5SSimon Horman #if IS_ENABLED(CONFIG_MPLS) 2438afe97e5SSimon Horman if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) 2448afe97e5SSimon Horman pr_err("tunnelmpls4 close: can't remove protocol\n"); 2458afe97e5SSimon Horman #endif 246dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 247c0d56408SKazunori MIYAZAWA if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6)) 248058bd4d2SJoe Perches pr_err("tunnel64 close: can't remove protocol\n"); 249c0d56408SKazunori MIYAZAWA #endif 250d2acc347SHerbert Xu if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP)) 251058bd4d2SJoe Perches pr_err("tunnel4 close: can't remove protocol\n"); 252d2acc347SHerbert Xu } 253d2acc347SHerbert Xu 254d2acc347SHerbert Xu module_init(tunnel4_init); 255d2acc347SHerbert Xu module_exit(tunnel4_fini); 256d2acc347SHerbert Xu MODULE_LICENSE("GPL"); 257