162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* xfrm4_tunnel.c: Generic IP tunnel transformer.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2003 David S. Miller (davem@redhat.com)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#define pr_fmt(fmt) "IPsec: " fmt
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/skbuff.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <net/xfrm.h>
1262306a36Sopenharmony_ci#include <net/protocol.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int ipip_output(struct xfrm_state *x, struct sk_buff *skb)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	skb_push(skb, -skb_network_offset(skb));
1762306a36Sopenharmony_ci	return 0;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int ipip_xfrm_rcv(struct xfrm_state *x, struct sk_buff *skb)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return ip_hdr(skb)->protocol;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int ipip_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	if (x->props.mode != XFRM_MODE_TUNNEL) {
2862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv4 tunnel can only be used with tunnel mode");
2962306a36Sopenharmony_ci		return -EINVAL;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (x->encap) {
3362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv4 tunnel is not compatible with encapsulation");
3462306a36Sopenharmony_ci		return -EINVAL;
3562306a36Sopenharmony_ci	}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	x->props.header_len = sizeof(struct iphdr);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void ipip_destroy(struct xfrm_state *x)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic const struct xfrm_type ipip_type = {
4762306a36Sopenharmony_ci	.owner		= THIS_MODULE,
4862306a36Sopenharmony_ci	.proto	     	= IPPROTO_IPIP,
4962306a36Sopenharmony_ci	.init_state	= ipip_init_state,
5062306a36Sopenharmony_ci	.destructor	= ipip_destroy,
5162306a36Sopenharmony_ci	.input		= ipip_xfrm_rcv,
5262306a36Sopenharmony_ci	.output		= ipip_output
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int xfrm_tunnel_rcv(struct sk_buff *skb)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return xfrm4_rcv_spi(skb, IPPROTO_IPIP, ip_hdr(skb)->saddr);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int xfrm_tunnel_err(struct sk_buff *skb, u32 info)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	return -ENOENT;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct xfrm_tunnel xfrm_tunnel_handler __read_mostly = {
6662306a36Sopenharmony_ci	.handler	=	xfrm_tunnel_rcv,
6762306a36Sopenharmony_ci	.err_handler	=	xfrm_tunnel_err,
6862306a36Sopenharmony_ci	.priority	=	4,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
7262306a36Sopenharmony_cistatic struct xfrm_tunnel xfrm64_tunnel_handler __read_mostly = {
7362306a36Sopenharmony_ci	.handler	=	xfrm_tunnel_rcv,
7462306a36Sopenharmony_ci	.err_handler	=	xfrm_tunnel_err,
7562306a36Sopenharmony_ci	.priority	=	3,
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int __init ipip_init(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (xfrm_register_type(&ipip_type, AF_INET) < 0) {
8262306a36Sopenharmony_ci		pr_info("%s: can't add xfrm type\n", __func__);
8362306a36Sopenharmony_ci		return -EAGAIN;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (xfrm4_tunnel_register(&xfrm_tunnel_handler, AF_INET)) {
8762306a36Sopenharmony_ci		pr_info("%s: can't add xfrm handler for AF_INET\n", __func__);
8862306a36Sopenharmony_ci		xfrm_unregister_type(&ipip_type, AF_INET);
8962306a36Sopenharmony_ci		return -EAGAIN;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
9262306a36Sopenharmony_ci	if (xfrm4_tunnel_register(&xfrm64_tunnel_handler, AF_INET6)) {
9362306a36Sopenharmony_ci		pr_info("%s: can't add xfrm handler for AF_INET6\n", __func__);
9462306a36Sopenharmony_ci		xfrm4_tunnel_deregister(&xfrm_tunnel_handler, AF_INET);
9562306a36Sopenharmony_ci		xfrm_unregister_type(&ipip_type, AF_INET);
9662306a36Sopenharmony_ci		return -EAGAIN;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci#endif
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void __exit ipip_fini(void)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
10562306a36Sopenharmony_ci	if (xfrm4_tunnel_deregister(&xfrm64_tunnel_handler, AF_INET6))
10662306a36Sopenharmony_ci		pr_info("%s: can't remove xfrm handler for AF_INET6\n",
10762306a36Sopenharmony_ci			__func__);
10862306a36Sopenharmony_ci#endif
10962306a36Sopenharmony_ci	if (xfrm4_tunnel_deregister(&xfrm_tunnel_handler, AF_INET))
11062306a36Sopenharmony_ci		pr_info("%s: can't remove xfrm handler for AF_INET\n",
11162306a36Sopenharmony_ci			__func__);
11262306a36Sopenharmony_ci	xfrm_unregister_type(&ipip_type, AF_INET);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cimodule_init(ipip_init);
11662306a36Sopenharmony_cimodule_exit(ipip_fini);
11762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
11862306a36Sopenharmony_ciMODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_IPIP);
119