GNU Linux-libre 5.10.217-gnu1
[releases.git] / net / netfilter / nft_reject_inet.c
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), nft_sk(pkt),
32                                       pkt->skb, nft_hook(pkt));
33                         break;
34                 case NFT_REJECT_ICMPX_UNREACH:
35                         nf_send_unreach(pkt->skb,
36                                         nft_reject_icmp_code(priv->icmp_code),
37                                         nft_hook(pkt));
38                         break;
39                 }
40                 break;
41         case NFPROTO_IPV6:
42                 switch (priv->type) {
43                 case NFT_REJECT_ICMP_UNREACH:
44                         nf_send_unreach6(nft_net(pkt), pkt->skb,
45                                          priv->icmp_code, nft_hook(pkt));
46                         break;
47                 case NFT_REJECT_TCP_RST:
48                         nf_send_reset6(nft_net(pkt), nft_sk(pkt),
49                                        pkt->skb, nft_hook(pkt));
50                         break;
51                 case NFT_REJECT_ICMPX_UNREACH:
52                         nf_send_unreach6(nft_net(pkt), pkt->skb,
53                                          nft_reject_icmpv6_code(priv->icmp_code),
54                                          nft_hook(pkt));
55                         break;
56                 }
57                 break;
58         }
59
60         regs->verdict.code = NF_DROP;
61 }
62
63 static int nft_reject_inet_init(const struct nft_ctx *ctx,
64                                 const struct nft_expr *expr,
65                                 const struct nlattr * const tb[])
66 {
67         struct nft_reject *priv = nft_expr_priv(expr);
68         int icmp_code;
69
70         if (tb[NFTA_REJECT_TYPE] == NULL)
71                 return -EINVAL;
72
73         priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
74         switch (priv->type) {
75         case NFT_REJECT_ICMP_UNREACH:
76         case NFT_REJECT_ICMPX_UNREACH:
77                 if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
78                         return -EINVAL;
79
80                 icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
81                 if (priv->type == NFT_REJECT_ICMPX_UNREACH &&
82                     icmp_code > NFT_REJECT_ICMPX_MAX)
83                         return -EINVAL;
84
85                 priv->icmp_code = icmp_code;
86                 break;
87         case NFT_REJECT_TCP_RST:
88                 break;
89         default:
90                 return -EINVAL;
91         }
92         return 0;
93 }
94
95 static int nft_reject_inet_dump(struct sk_buff *skb,
96                                 const struct nft_expr *expr)
97 {
98         const struct nft_reject *priv = nft_expr_priv(expr);
99
100         if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type)))
101                 goto nla_put_failure;
102
103         switch (priv->type) {
104         case NFT_REJECT_ICMP_UNREACH:
105         case NFT_REJECT_ICMPX_UNREACH:
106                 if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
107                         goto nla_put_failure;
108                 break;
109         default:
110                 break;
111         }
112
113         return 0;
114
115 nla_put_failure:
116         return -1;
117 }
118
119 static struct nft_expr_type nft_reject_inet_type;
120 static const struct nft_expr_ops nft_reject_inet_ops = {
121         .type           = &nft_reject_inet_type,
122         .size           = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
123         .eval           = nft_reject_inet_eval,
124         .init           = nft_reject_inet_init,
125         .dump           = nft_reject_inet_dump,
126         .validate       = nft_reject_validate,
127 };
128
129 static struct nft_expr_type nft_reject_inet_type __read_mostly = {
130         .family         = NFPROTO_INET,
131         .name           = "reject",
132         .ops            = &nft_reject_inet_ops,
133         .policy         = nft_reject_policy,
134         .maxattr        = NFTA_REJECT_MAX,
135         .owner          = THIS_MODULE,
136 };
137
138 static int __init nft_reject_inet_module_init(void)
139 {
140         return nft_register_expr(&nft_reject_inet_type);
141 }
142
143 static void __exit nft_reject_inet_module_exit(void)
144 {
145         nft_unregister_expr(&nft_reject_inet_type);
146 }
147
148 module_init(nft_reject_inet_module_init);
149 module_exit(nft_reject_inet_module_exit);
150
151 MODULE_LICENSE("GPL");
152 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
153 MODULE_ALIAS_NFT_AF_EXPR(1, "reject");
154 MODULE_DESCRIPTION("Netfilter nftables reject inet support");