1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2014 Patrick McHardy <kaber@trash.net>
4  */
5 
6 #include <linux/kernel.h>
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/netlink.h>
10 #include <linux/netfilter.h>
11 #include <linux/netfilter/nf_tables.h>
12 #include <net/netfilter/nf_tables.h>
13 #include <net/netfilter/nft_reject.h>
14 #include <net/netfilter/ipv4/nf_reject.h>
15 #include <net/netfilter/ipv6/nf_reject.h>
16 
17 static void nft_reject_inet_eval(const struct nft_expr *expr,
18 				 struct nft_regs *regs,
19 				 const struct nft_pktinfo *pkt)
20 {
21 	struct nft_reject *priv = nft_expr_priv(expr);
22 
23 	switch (nft_pf(pkt)) {
24 	case NFPROTO_IPV4:
25 		switch (priv->type) {
26 		case NFT_REJECT_ICMP_UNREACH:
27 			nf_send_unreach(pkt->skb, priv->icmp_code,
28 					nft_hook(pkt));
29 			break;
30 		case NFT_REJECT_TCP_RST:
31 			nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt));
32 			break;
33 		case NFT_REJECT_ICMPX_UNREACH:
34 			nf_send_unreach(pkt->skb,
35 					nft_reject_icmp_code(priv->icmp_code),
36 					nft_hook(pkt));
37 			break;
38 		}
39 		break;
40 	case NFPROTO_IPV6:
41 		switch (priv->type) {
42 		case NFT_REJECT_ICMP_UNREACH:
43 			nf_send_unreach6(nft_net(pkt), pkt->skb,
44 					 priv->icmp_code, nft_hook(pkt));
45 			break;
46 		case NFT_REJECT_TCP_RST:
47 			nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt));
48 			break;
49 		case NFT_REJECT_ICMPX_UNREACH:
50 			nf_send_unreach6(nft_net(pkt), pkt->skb,
51 					 nft_reject_icmpv6_code(priv->icmp_code),
52 					 nft_hook(pkt));
53 			break;
54 		}
55 		break;
56 	}
57 
58 	regs->verdict.code = NF_DROP;
59 }
60 
61 static int nft_reject_inet_init(const struct nft_ctx *ctx,
62 				const struct nft_expr *expr,
63 				const struct nlattr * const tb[])
64 {
65 	struct nft_reject *priv = nft_expr_priv(expr);
66 	int icmp_code;
67 
68 	if (tb[NFTA_REJECT_TYPE] == NULL)
69 		return -EINVAL;
70 
71 	priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
72 	switch (priv->type) {
73 	case NFT_REJECT_ICMP_UNREACH:
74 	case NFT_REJECT_ICMPX_UNREACH:
75 		if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
76 			return -EINVAL;
77 
78 		icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
79 		if (priv->type == NFT_REJECT_ICMPX_UNREACH &&
80 		    icmp_code > NFT_REJECT_ICMPX_MAX)
81 			return -EINVAL;
82 
83 		priv->icmp_code = icmp_code;
84 		break;
85 	case NFT_REJECT_TCP_RST:
86 		break;
87 	default:
88 		return -EINVAL;
89 	}
90 	return 0;
91 }
92 
93 static int nft_reject_inet_dump(struct sk_buff *skb,
94 				const struct nft_expr *expr)
95 {
96 	const struct nft_reject *priv = nft_expr_priv(expr);
97 
98 	if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type)))
99 		goto nla_put_failure;
100 
101 	switch (priv->type) {
102 	case NFT_REJECT_ICMP_UNREACH:
103 	case NFT_REJECT_ICMPX_UNREACH:
104 		if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
105 			goto nla_put_failure;
106 		break;
107 	default:
108 		break;
109 	}
110 
111 	return 0;
112 
113 nla_put_failure:
114 	return -1;
115 }
116 
117 static struct nft_expr_type nft_reject_inet_type;
118 static const struct nft_expr_ops nft_reject_inet_ops = {
119 	.type		= &nft_reject_inet_type,
120 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_reject)),
121 	.eval		= nft_reject_inet_eval,
122 	.init		= nft_reject_inet_init,
123 	.dump		= nft_reject_inet_dump,
124 	.validate	= nft_reject_validate,
125 };
126 
127 static struct nft_expr_type nft_reject_inet_type __read_mostly = {
128 	.family		= NFPROTO_INET,
129 	.name		= "reject",
130 	.ops		= &nft_reject_inet_ops,
131 	.policy		= nft_reject_policy,
132 	.maxattr	= NFTA_REJECT_MAX,
133 	.owner		= THIS_MODULE,
134 };
135 
136 static int __init nft_reject_inet_module_init(void)
137 {
138 	return nft_register_expr(&nft_reject_inet_type);
139 }
140 
141 static void __exit nft_reject_inet_module_exit(void)
142 {
143 	nft_unregister_expr(&nft_reject_inet_type);
144 }
145 
146 module_init(nft_reject_inet_module_init);
147 module_exit(nft_reject_inet_module_exit);
148 
149 MODULE_LICENSE("GPL");
150 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
151 MODULE_ALIAS_NFT_AF_EXPR(1, "reject");
152 MODULE_DESCRIPTION("Netfilter nftables reject inet support");
153