Merge tag 'm68knommu-for-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / net / netfilter / nft_reject_netdev.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2020 Laura Garcia Liebana <nevola@gmail.com>
4  * Copyright (c) 2020 Jose M. Guisado <guigom@riseup.net>
5  */
6
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/netlink.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter/nf_tables.h>
13 #include <net/netfilter/nf_tables.h>
14 #include <net/netfilter/nft_reject.h>
15 #include <net/netfilter/ipv4/nf_reject.h>
16 #include <net/netfilter/ipv6/nf_reject.h>
17
18 static void nft_reject_queue_xmit(struct sk_buff *nskb, struct sk_buff *oldskb)
19 {
20         dev_hard_header(nskb, nskb->dev, ntohs(oldskb->protocol),
21                         eth_hdr(oldskb)->h_source, eth_hdr(oldskb)->h_dest,
22                         nskb->len);
23         dev_queue_xmit(nskb);
24 }
25
26 static void nft_reject_netdev_send_v4_tcp_reset(struct net *net,
27                                                 struct sk_buff *oldskb,
28                                                 const struct net_device *dev,
29                                                 int hook)
30 {
31         struct sk_buff *nskb;
32
33         nskb = nf_reject_skb_v4_tcp_reset(net, oldskb, dev, hook);
34         if (!nskb)
35                 return;
36
37         nft_reject_queue_xmit(nskb, oldskb);
38 }
39
40 static void nft_reject_netdev_send_v4_unreach(struct net *net,
41                                               struct sk_buff *oldskb,
42                                               const struct net_device *dev,
43                                               int hook, u8 code)
44 {
45         struct sk_buff *nskb;
46
47         nskb = nf_reject_skb_v4_unreach(net, oldskb, dev, hook, code);
48         if (!nskb)
49                 return;
50
51         nft_reject_queue_xmit(nskb, oldskb);
52 }
53
54 static void nft_reject_netdev_send_v6_tcp_reset(struct net *net,
55                                                 struct sk_buff *oldskb,
56                                                 const struct net_device *dev,
57                                                 int hook)
58 {
59         struct sk_buff *nskb;
60
61         nskb = nf_reject_skb_v6_tcp_reset(net, oldskb, dev, hook);
62         if (!nskb)
63                 return;
64
65         nft_reject_queue_xmit(nskb, oldskb);
66 }
67
68
69 static void nft_reject_netdev_send_v6_unreach(struct net *net,
70                                               struct sk_buff *oldskb,
71                                               const struct net_device *dev,
72                                               int hook, u8 code)
73 {
74         struct sk_buff *nskb;
75
76         nskb = nf_reject_skb_v6_unreach(net, oldskb, dev, hook, code);
77         if (!nskb)
78                 return;
79
80         nft_reject_queue_xmit(nskb, oldskb);
81 }
82
83 static void nft_reject_netdev_eval(const struct nft_expr *expr,
84                                    struct nft_regs *regs,
85                                    const struct nft_pktinfo *pkt)
86 {
87         struct ethhdr *eth = eth_hdr(pkt->skb);
88         struct nft_reject *priv = nft_expr_priv(expr);
89         const unsigned char *dest = eth->h_dest;
90
91         if (is_broadcast_ether_addr(dest) ||
92             is_multicast_ether_addr(dest))
93                 goto out;
94
95         switch (eth->h_proto) {
96         case htons(ETH_P_IP):
97                 switch (priv->type) {
98                 case NFT_REJECT_ICMP_UNREACH:
99                         nft_reject_netdev_send_v4_unreach(nft_net(pkt), pkt->skb,
100                                                           nft_in(pkt),
101                                                           nft_hook(pkt),
102                                                           priv->icmp_code);
103                         break;
104                 case NFT_REJECT_TCP_RST:
105                         nft_reject_netdev_send_v4_tcp_reset(nft_net(pkt), pkt->skb,
106                                                             nft_in(pkt),
107                                                             nft_hook(pkt));
108                         break;
109                 case NFT_REJECT_ICMPX_UNREACH:
110                         nft_reject_netdev_send_v4_unreach(nft_net(pkt), pkt->skb,
111                                                           nft_in(pkt),
112                                                           nft_hook(pkt),
113                                                           nft_reject_icmp_code(priv->icmp_code));
114                         break;
115                 }
116                 break;
117         case htons(ETH_P_IPV6):
118                 switch (priv->type) {
119                 case NFT_REJECT_ICMP_UNREACH:
120                         nft_reject_netdev_send_v6_unreach(nft_net(pkt), pkt->skb,
121                                                           nft_in(pkt),
122                                                           nft_hook(pkt),
123                                                           priv->icmp_code);
124                         break;
125                 case NFT_REJECT_TCP_RST:
126                         nft_reject_netdev_send_v6_tcp_reset(nft_net(pkt), pkt->skb,
127                                                             nft_in(pkt),
128                                                             nft_hook(pkt));
129                         break;
130                 case NFT_REJECT_ICMPX_UNREACH:
131                         nft_reject_netdev_send_v6_unreach(nft_net(pkt), pkt->skb,
132                                                           nft_in(pkt),
133                                                           nft_hook(pkt),
134                                                           nft_reject_icmpv6_code(priv->icmp_code));
135                         break;
136                 }
137                 break;
138         default:
139                 /* No explicit way to reject this protocol, drop it. */
140                 break;
141         }
142 out:
143         regs->verdict.code = NF_DROP;
144 }
145
146 static int nft_reject_netdev_validate(const struct nft_ctx *ctx,
147                                       const struct nft_expr *expr,
148                                       const struct nft_data **data)
149 {
150         return nft_chain_validate_hooks(ctx->chain, (1 << NF_NETDEV_INGRESS));
151 }
152
153 static struct nft_expr_type nft_reject_netdev_type;
154 static const struct nft_expr_ops nft_reject_netdev_ops = {
155         .type           = &nft_reject_netdev_type,
156         .size           = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
157         .eval           = nft_reject_netdev_eval,
158         .init           = nft_reject_init,
159         .dump           = nft_reject_dump,
160         .validate       = nft_reject_netdev_validate,
161 };
162
163 static struct nft_expr_type nft_reject_netdev_type __read_mostly = {
164         .family         = NFPROTO_NETDEV,
165         .name           = "reject",
166         .ops            = &nft_reject_netdev_ops,
167         .policy         = nft_reject_policy,
168         .maxattr        = NFTA_REJECT_MAX,
169         .owner          = THIS_MODULE,
170 };
171
172 static int __init nft_reject_netdev_module_init(void)
173 {
174         return nft_register_expr(&nft_reject_netdev_type);
175 }
176
177 static void __exit nft_reject_netdev_module_exit(void)
178 {
179         nft_unregister_expr(&nft_reject_netdev_type);
180 }
181
182 module_init(nft_reject_netdev_module_init);
183 module_exit(nft_reject_netdev_module_exit);
184
185 MODULE_LICENSE("GPL");
186 MODULE_AUTHOR("Laura Garcia Liebana <nevola@gmail.com>");
187 MODULE_AUTHOR("Jose M. Guisado <guigom@riseup.net>");
188 MODULE_DESCRIPTION("Reject packets from netdev via nftables");
189 MODULE_ALIAS_NFT_AF_EXPR(5, "reject");