Merge tag 'linux-kselftest-kunit-5.12-rc1' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / net / bridge / netfilter / nft_reject_bridge.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
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 #include <linux/ip.h>
17 #include <net/ip.h>
18 #include <net/ip6_checksum.h>
19 #include <linux/netfilter_bridge.h>
20 #include <linux/netfilter_ipv6.h>
21 #include "../br_private.h"
22
23 static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
24                                         struct sk_buff *nskb)
25 {
26         struct ethhdr *eth;
27
28         eth = skb_push(nskb, ETH_HLEN);
29         skb_reset_mac_header(nskb);
30         ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest);
31         ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source);
32         eth->h_proto = eth_hdr(oldskb)->h_proto;
33         skb_pull(nskb, ETH_HLEN);
34
35         if (skb_vlan_tag_present(oldskb)) {
36                 u16 vid = skb_vlan_tag_get(oldskb);
37
38                 __vlan_hwaccel_put_tag(nskb, oldskb->vlan_proto, vid);
39         }
40 }
41
42 /* We cannot use oldskb->dev, it can be either bridge device (NF_BRIDGE INPUT)
43  * or the bridge port (NF_BRIDGE PREROUTING).
44  */
45 static void nft_reject_br_send_v4_tcp_reset(struct net *net,
46                                             struct sk_buff *oldskb,
47                                             const struct net_device *dev,
48                                             int hook)
49 {
50         struct sk_buff *nskb;
51
52         nskb = nf_reject_skb_v4_tcp_reset(net, oldskb, dev, hook);
53         if (!nskb)
54                 return;
55
56         nft_reject_br_push_etherhdr(oldskb, nskb);
57
58         br_forward(br_port_get_rcu(dev), nskb, false, true);
59 }
60
61 static void nft_reject_br_send_v4_unreach(struct net *net,
62                                           struct sk_buff *oldskb,
63                                           const struct net_device *dev,
64                                           int hook, u8 code)
65 {
66         struct sk_buff *nskb;
67
68         nskb = nf_reject_skb_v4_unreach(net, oldskb, dev, hook, code);
69         if (!nskb)
70                 return;
71
72         nft_reject_br_push_etherhdr(oldskb, nskb);
73
74         br_forward(br_port_get_rcu(dev), nskb, false, true);
75 }
76
77 static void nft_reject_br_send_v6_tcp_reset(struct net *net,
78                                             struct sk_buff *oldskb,
79                                             const struct net_device *dev,
80                                             int hook)
81 {
82         struct sk_buff *nskb;
83
84         nskb = nf_reject_skb_v6_tcp_reset(net, oldskb, dev, hook);
85         if (!nskb)
86                 return;
87
88         nft_reject_br_push_etherhdr(oldskb, nskb);
89
90         br_forward(br_port_get_rcu(dev), nskb, false, true);
91 }
92
93
94 static void nft_reject_br_send_v6_unreach(struct net *net,
95                                           struct sk_buff *oldskb,
96                                           const struct net_device *dev,
97                                           int hook, u8 code)
98 {
99         struct sk_buff *nskb;
100
101         nskb = nf_reject_skb_v6_unreach(net, oldskb, dev, hook, code);
102         if (!nskb)
103                 return;
104
105         nft_reject_br_push_etherhdr(oldskb, nskb);
106
107         br_forward(br_port_get_rcu(dev), nskb, false, true);
108 }
109
110 static void nft_reject_bridge_eval(const struct nft_expr *expr,
111                                    struct nft_regs *regs,
112                                    const struct nft_pktinfo *pkt)
113 {
114         struct nft_reject *priv = nft_expr_priv(expr);
115         const unsigned char *dest = eth_hdr(pkt->skb)->h_dest;
116
117         if (is_broadcast_ether_addr(dest) ||
118             is_multicast_ether_addr(dest))
119                 goto out;
120
121         switch (eth_hdr(pkt->skb)->h_proto) {
122         case htons(ETH_P_IP):
123                 switch (priv->type) {
124                 case NFT_REJECT_ICMP_UNREACH:
125                         nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb,
126                                                       nft_in(pkt),
127                                                       nft_hook(pkt),
128                                                       priv->icmp_code);
129                         break;
130                 case NFT_REJECT_TCP_RST:
131                         nft_reject_br_send_v4_tcp_reset(nft_net(pkt), pkt->skb,
132                                                         nft_in(pkt),
133                                                         nft_hook(pkt));
134                         break;
135                 case NFT_REJECT_ICMPX_UNREACH:
136                         nft_reject_br_send_v4_unreach(nft_net(pkt), pkt->skb,
137                                                       nft_in(pkt),
138                                                       nft_hook(pkt),
139                                                       nft_reject_icmp_code(priv->icmp_code));
140                         break;
141                 }
142                 break;
143         case htons(ETH_P_IPV6):
144                 switch (priv->type) {
145                 case NFT_REJECT_ICMP_UNREACH:
146                         nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb,
147                                                       nft_in(pkt),
148                                                       nft_hook(pkt),
149                                                       priv->icmp_code);
150                         break;
151                 case NFT_REJECT_TCP_RST:
152                         nft_reject_br_send_v6_tcp_reset(nft_net(pkt), pkt->skb,
153                                                         nft_in(pkt),
154                                                         nft_hook(pkt));
155                         break;
156                 case NFT_REJECT_ICMPX_UNREACH:
157                         nft_reject_br_send_v6_unreach(nft_net(pkt), pkt->skb,
158                                                       nft_in(pkt),
159                                                       nft_hook(pkt),
160                                                       nft_reject_icmpv6_code(priv->icmp_code));
161                         break;
162                 }
163                 break;
164         default:
165                 /* No explicit way to reject this protocol, drop it. */
166                 break;
167         }
168 out:
169         regs->verdict.code = NF_DROP;
170 }
171
172 static int nft_reject_bridge_validate(const struct nft_ctx *ctx,
173                                       const struct nft_expr *expr,
174                                       const struct nft_data **data)
175 {
176         return nft_chain_validate_hooks(ctx->chain, (1 << NF_BR_PRE_ROUTING) |
177                                                     (1 << NF_BR_LOCAL_IN));
178 }
179
180 static struct nft_expr_type nft_reject_bridge_type;
181 static const struct nft_expr_ops nft_reject_bridge_ops = {
182         .type           = &nft_reject_bridge_type,
183         .size           = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
184         .eval           = nft_reject_bridge_eval,
185         .init           = nft_reject_init,
186         .dump           = nft_reject_dump,
187         .validate       = nft_reject_bridge_validate,
188 };
189
190 static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
191         .family         = NFPROTO_BRIDGE,
192         .name           = "reject",
193         .ops            = &nft_reject_bridge_ops,
194         .policy         = nft_reject_policy,
195         .maxattr        = NFTA_REJECT_MAX,
196         .owner          = THIS_MODULE,
197 };
198
199 static int __init nft_reject_bridge_module_init(void)
200 {
201         return nft_register_expr(&nft_reject_bridge_type);
202 }
203
204 static void __exit nft_reject_bridge_module_exit(void)
205 {
206         nft_unregister_expr(&nft_reject_bridge_type);
207 }
208
209 module_init(nft_reject_bridge_module_init);
210 module_exit(nft_reject_bridge_module_exit);
211
212 MODULE_LICENSE("GPL");
213 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
214 MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject");
215 MODULE_DESCRIPTION("Reject packets from bridge via nftables");