162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* tunnel4.c: Generic IP tunnel transformer.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2003 David S. Miller (davem@redhat.com)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/mutex.h>
1062306a36Sopenharmony_ci#include <linux/mpls.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/skbuff.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <net/icmp.h>
1562306a36Sopenharmony_ci#include <net/ip.h>
1662306a36Sopenharmony_ci#include <net/protocol.h>
1762306a36Sopenharmony_ci#include <net/xfrm.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic struct xfrm_tunnel __rcu *tunnel4_handlers __read_mostly;
2062306a36Sopenharmony_cistatic struct xfrm_tunnel __rcu *tunnel64_handlers __read_mostly;
2162306a36Sopenharmony_cistatic struct xfrm_tunnel __rcu *tunnelmpls4_handlers __read_mostly;
2262306a36Sopenharmony_cistatic DEFINE_MUTEX(tunnel4_mutex);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic inline struct xfrm_tunnel __rcu **fam_handlers(unsigned short family)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return (family == AF_INET) ? &tunnel4_handlers :
2762306a36Sopenharmony_ci		(family == AF_INET6) ? &tunnel64_handlers :
2862306a36Sopenharmony_ci		&tunnelmpls4_handlers;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct xfrm_tunnel __rcu **pprev;
3462306a36Sopenharmony_ci	struct xfrm_tunnel *t;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	int ret = -EEXIST;
3762306a36Sopenharmony_ci	int priority = handler->priority;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	mutex_lock(&tunnel4_mutex);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (pprev = fam_handlers(family);
4262306a36Sopenharmony_ci	     (t = rcu_dereference_protected(*pprev,
4362306a36Sopenharmony_ci			lockdep_is_held(&tunnel4_mutex))) != NULL;
4462306a36Sopenharmony_ci	     pprev = &t->next) {
4562306a36Sopenharmony_ci		if (t->priority > priority)
4662306a36Sopenharmony_ci			break;
4762306a36Sopenharmony_ci		if (t->priority == priority)
4862306a36Sopenharmony_ci			goto err;
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	handler->next = *pprev;
5262306a36Sopenharmony_ci	rcu_assign_pointer(*pprev, handler);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	ret = 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cierr:
5762306a36Sopenharmony_ci	mutex_unlock(&tunnel4_mutex);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return ret;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm4_tunnel_register);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciint xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct xfrm_tunnel __rcu **pprev;
6662306a36Sopenharmony_ci	struct xfrm_tunnel *t;
6762306a36Sopenharmony_ci	int ret = -ENOENT;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	mutex_lock(&tunnel4_mutex);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	for (pprev = fam_handlers(family);
7262306a36Sopenharmony_ci	     (t = rcu_dereference_protected(*pprev,
7362306a36Sopenharmony_ci			lockdep_is_held(&tunnel4_mutex))) != NULL;
7462306a36Sopenharmony_ci	     pprev = &t->next) {
7562306a36Sopenharmony_ci		if (t == handler) {
7662306a36Sopenharmony_ci			*pprev = handler->next;
7762306a36Sopenharmony_ci			ret = 0;
7862306a36Sopenharmony_ci			break;
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	mutex_unlock(&tunnel4_mutex);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	synchronize_net();
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return ret;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ciEXPORT_SYMBOL(xfrm4_tunnel_deregister);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define for_each_tunnel_rcu(head, handler)		\
9162306a36Sopenharmony_ci	for (handler = rcu_dereference(head);		\
9262306a36Sopenharmony_ci	     handler != NULL;				\
9362306a36Sopenharmony_ci	     handler = rcu_dereference(handler->next))	\
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int tunnel4_rcv(struct sk_buff *skb)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct iphdr)))
10062306a36Sopenharmony_ci		goto drop;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnel4_handlers, handler)
10362306a36Sopenharmony_ci		if (!handler->handler(skb))
10462306a36Sopenharmony_ci			return 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cidrop:
10962306a36Sopenharmony_ci	kfree_skb(skb);
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
11462306a36Sopenharmony_cistatic int tunnel4_rcv_cb(struct sk_buff *skb, u8 proto, int err)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct xfrm_tunnel __rcu *head;
11762306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	head = (proto == IPPROTO_IPIP) ? tunnel4_handlers : tunnel64_handlers;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for_each_tunnel_rcu(head, handler) {
12362306a36Sopenharmony_ci		if (handler->cb_handler) {
12462306a36Sopenharmony_ci			ret = handler->cb_handler(skb, err);
12562306a36Sopenharmony_ci			if (ret <= 0)
12662306a36Sopenharmony_ci				return ret;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const struct xfrm_input_afinfo tunnel4_input_afinfo = {
13462306a36Sopenharmony_ci	.family		=	AF_INET,
13562306a36Sopenharmony_ci	.is_ipip	=	true,
13662306a36Sopenharmony_ci	.callback	=	tunnel4_rcv_cb,
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci#endif
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
14162306a36Sopenharmony_cistatic int tunnel64_rcv(struct sk_buff *skb)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
14662306a36Sopenharmony_ci		goto drop;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnel64_handlers, handler)
14962306a36Sopenharmony_ci		if (!handler->handler(skb))
15062306a36Sopenharmony_ci			return 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cidrop:
15562306a36Sopenharmony_ci	kfree_skb(skb);
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
16162306a36Sopenharmony_cistatic int tunnelmpls4_rcv(struct sk_buff *skb)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!pskb_may_pull(skb, sizeof(struct mpls_label)))
16662306a36Sopenharmony_ci		goto drop;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
16962306a36Sopenharmony_ci		if (!handler->handler(skb))
17062306a36Sopenharmony_ci			return 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cidrop:
17562306a36Sopenharmony_ci	kfree_skb(skb);
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci#endif
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int tunnel4_err(struct sk_buff *skb, u32 info)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnel4_handlers, handler)
18562306a36Sopenharmony_ci		if (!handler->err_handler(skb, info))
18662306a36Sopenharmony_ci			return 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return -ENOENT;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
19262306a36Sopenharmony_cistatic int tunnel64_err(struct sk_buff *skb, u32 info)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnel64_handlers, handler)
19762306a36Sopenharmony_ci		if (!handler->err_handler(skb, info))
19862306a36Sopenharmony_ci			return 0;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return -ENOENT;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci#endif
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
20562306a36Sopenharmony_cistatic int tunnelmpls4_err(struct sk_buff *skb, u32 info)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct xfrm_tunnel *handler;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	for_each_tunnel_rcu(tunnelmpls4_handlers, handler)
21062306a36Sopenharmony_ci		if (!handler->err_handler(skb, info))
21162306a36Sopenharmony_ci			return 0;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return -ENOENT;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci#endif
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic const struct net_protocol tunnel4_protocol = {
21862306a36Sopenharmony_ci	.handler	=	tunnel4_rcv,
21962306a36Sopenharmony_ci	.err_handler	=	tunnel4_err,
22062306a36Sopenharmony_ci	.no_policy	=	1,
22162306a36Sopenharmony_ci};
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
22462306a36Sopenharmony_cistatic const struct net_protocol tunnel64_protocol = {
22562306a36Sopenharmony_ci	.handler	=	tunnel64_rcv,
22662306a36Sopenharmony_ci	.err_handler	=	tunnel64_err,
22762306a36Sopenharmony_ci	.no_policy	=	1,
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci#endif
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
23262306a36Sopenharmony_cistatic const struct net_protocol tunnelmpls4_protocol = {
23362306a36Sopenharmony_ci	.handler	=	tunnelmpls4_rcv,
23462306a36Sopenharmony_ci	.err_handler	=	tunnelmpls4_err,
23562306a36Sopenharmony_ci	.no_policy	=	1,
23662306a36Sopenharmony_ci};
23762306a36Sopenharmony_ci#endif
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int __init tunnel4_init(void)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	if (inet_add_protocol(&tunnel4_protocol, IPPROTO_IPIP))
24262306a36Sopenharmony_ci		goto err;
24362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
24462306a36Sopenharmony_ci	if (inet_add_protocol(&tunnel64_protocol, IPPROTO_IPV6)) {
24562306a36Sopenharmony_ci		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
24662306a36Sopenharmony_ci		goto err;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci#endif
24962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
25062306a36Sopenharmony_ci	if (inet_add_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS)) {
25162306a36Sopenharmony_ci		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
25262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
25362306a36Sopenharmony_ci		inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6);
25462306a36Sopenharmony_ci#endif
25562306a36Sopenharmony_ci		goto err;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci#endif
25862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
25962306a36Sopenharmony_ci	if (xfrm_input_register_afinfo(&tunnel4_input_afinfo)) {
26062306a36Sopenharmony_ci		inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP);
26162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
26262306a36Sopenharmony_ci		inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6);
26362306a36Sopenharmony_ci#endif
26462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
26562306a36Sopenharmony_ci		inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS);
26662306a36Sopenharmony_ci#endif
26762306a36Sopenharmony_ci		goto err;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci#endif
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cierr:
27362306a36Sopenharmony_ci	pr_err("%s: can't add protocol\n", __func__);
27462306a36Sopenharmony_ci	return -EAGAIN;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void __exit tunnel4_fini(void)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_INET_XFRM_TUNNEL)
28062306a36Sopenharmony_ci	if (xfrm_input_unregister_afinfo(&tunnel4_input_afinfo))
28162306a36Sopenharmony_ci		pr_err("tunnel4 close: can't remove input afinfo\n");
28262306a36Sopenharmony_ci#endif
28362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MPLS)
28462306a36Sopenharmony_ci	if (inet_del_protocol(&tunnelmpls4_protocol, IPPROTO_MPLS))
28562306a36Sopenharmony_ci		pr_err("tunnelmpls4 close: can't remove protocol\n");
28662306a36Sopenharmony_ci#endif
28762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
28862306a36Sopenharmony_ci	if (inet_del_protocol(&tunnel64_protocol, IPPROTO_IPV6))
28962306a36Sopenharmony_ci		pr_err("tunnel64 close: can't remove protocol\n");
29062306a36Sopenharmony_ci#endif
29162306a36Sopenharmony_ci	if (inet_del_protocol(&tunnel4_protocol, IPPROTO_IPIP))
29262306a36Sopenharmony_ci		pr_err("tunnel4 close: can't remove protocol\n");
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cimodule_init(tunnel4_init);
29662306a36Sopenharmony_cimodule_exit(tunnel4_fini);
29762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
298