Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2014 Pablo Neira Ayuso <pablo@netfilter.org> |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 as |
| 6 | * published by the Free Software Foundation. |
| 7 | */ |
| 8 | |
| 9 | #include <linux/kernel.h> |
| 10 | #include <linux/init.h> |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/netlink.h> |
| 13 | #include <linux/netfilter.h> |
| 14 | #include <linux/netfilter/nf_tables.h> |
| 15 | #include <net/netfilter/nf_tables.h> |
| 16 | #include <net/netfilter/nft_reject.h> |
Alvaro Neira | 68b0faa | 2014-11-26 10:21:36 +0100 | [diff] [blame] | 17 | #include <net/netfilter/nf_tables_bridge.h> |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 18 | #include <net/netfilter/ipv4/nf_reject.h> |
| 19 | #include <net/netfilter/ipv6/nf_reject.h> |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 20 | #include <linux/ip.h> |
| 21 | #include <net/ip.h> |
Guenter Roeck | c1207c0 | 2014-11-02 18:19:15 -0800 | [diff] [blame] | 22 | #include <net/ip6_checksum.h> |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 23 | #include <linux/netfilter_bridge.h> |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 24 | #include <linux/netfilter_ipv6.h> |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 25 | #include "../br_private.h" |
| 26 | |
| 27 | static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb, |
| 28 | struct sk_buff *nskb) |
| 29 | { |
| 30 | struct ethhdr *eth; |
| 31 | |
| 32 | eth = (struct ethhdr *)skb_push(nskb, ETH_HLEN); |
| 33 | skb_reset_mac_header(nskb); |
| 34 | ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest); |
| 35 | ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source); |
| 36 | eth->h_proto = eth_hdr(oldskb)->h_proto; |
| 37 | skb_pull(nskb, ETH_HLEN); |
| 38 | } |
| 39 | |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 40 | /* We cannot use oldskb->dev, it can be either bridge device (NF_BRIDGE INPUT) |
| 41 | * or the bridge port (NF_BRIDGE PREROUTING). |
| 42 | */ |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 43 | static void nft_reject_br_send_v4_tcp_reset(struct net *net, |
| 44 | struct sk_buff *oldskb, |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 45 | const struct net_device *dev, |
| 46 | int hook) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 47 | { |
| 48 | struct sk_buff *nskb; |
| 49 | struct iphdr *niph; |
| 50 | const struct tcphdr *oth; |
| 51 | struct tcphdr _oth; |
| 52 | |
Alvaro Neira | 68b0faa | 2014-11-26 10:21:36 +0100 | [diff] [blame] | 53 | if (!nft_bridge_iphdr_validate(oldskb)) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 54 | return; |
| 55 | |
| 56 | oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); |
| 57 | if (!oth) |
| 58 | return; |
| 59 | |
| 60 | nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + |
| 61 | LL_MAX_HEADER, GFP_ATOMIC); |
| 62 | if (!nskb) |
| 63 | return; |
| 64 | |
| 65 | skb_reserve(nskb, LL_MAX_HEADER); |
| 66 | niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, |
Nikolay Borisov | fa50d97 | 2016-02-15 12:11:27 +0200 | [diff] [blame] | 67 | net->ipv4.sysctl_ip_default_ttl); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 68 | nf_reject_ip_tcphdr_put(nskb, oldskb, oth); |
Nikolay Borisov | fa50d97 | 2016-02-15 12:11:27 +0200 | [diff] [blame] | 69 | niph->ttl = net->ipv4.sysctl_ip_default_ttl; |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 70 | niph->tot_len = htons(nskb->len); |
| 71 | ip_send_check(niph); |
| 72 | |
| 73 | nft_reject_br_push_etherhdr(oldskb, nskb); |
| 74 | |
Nikolay Aleksandrov | 37b090e | 2016-07-14 06:10:02 +0300 | [diff] [blame] | 75 | br_forward(br_port_get_rcu(dev), nskb, false, true); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 76 | } |
| 77 | |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 78 | static void nft_reject_br_send_v4_unreach(struct net *net, |
| 79 | struct sk_buff *oldskb, |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 80 | const struct net_device *dev, |
| 81 | int hook, u8 code) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 82 | { |
| 83 | struct sk_buff *nskb; |
| 84 | struct iphdr *niph; |
| 85 | struct icmphdr *icmph; |
| 86 | unsigned int len; |
| 87 | void *payload; |
| 88 | __wsum csum; |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 89 | u8 proto; |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 90 | |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 91 | if (oldskb->csum_bad || !nft_bridge_iphdr_validate(oldskb)) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 92 | return; |
| 93 | |
| 94 | /* IP header checks: fragment. */ |
| 95 | if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) |
| 96 | return; |
| 97 | |
| 98 | /* RFC says return as much as we can without exceeding 576 bytes. */ |
| 99 | len = min_t(unsigned int, 536, oldskb->len); |
| 100 | |
| 101 | if (!pskb_may_pull(oldskb, len)) |
| 102 | return; |
| 103 | |
Florian Westphal | a03a8db | 2015-03-09 23:04:15 +0100 | [diff] [blame] | 104 | if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len))) |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 105 | return; |
| 106 | |
| 107 | if (ip_hdr(oldskb)->protocol == IPPROTO_TCP || |
| 108 | ip_hdr(oldskb)->protocol == IPPROTO_UDP) |
| 109 | proto = ip_hdr(oldskb)->protocol; |
| 110 | else |
| 111 | proto = 0; |
| 112 | |
| 113 | if (!skb_csum_unnecessary(oldskb) && |
| 114 | nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto)) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 115 | return; |
| 116 | |
| 117 | nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) + |
| 118 | LL_MAX_HEADER + len, GFP_ATOMIC); |
| 119 | if (!nskb) |
| 120 | return; |
| 121 | |
| 122 | skb_reserve(nskb, LL_MAX_HEADER); |
| 123 | niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP, |
Nikolay Borisov | fa50d97 | 2016-02-15 12:11:27 +0200 | [diff] [blame] | 124 | net->ipv4.sysctl_ip_default_ttl); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 125 | |
| 126 | skb_reset_transport_header(nskb); |
| 127 | icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr)); |
| 128 | memset(icmph, 0, sizeof(*icmph)); |
| 129 | icmph->type = ICMP_DEST_UNREACH; |
| 130 | icmph->code = code; |
| 131 | |
| 132 | payload = skb_put(nskb, len); |
| 133 | memcpy(payload, skb_network_header(oldskb), len); |
| 134 | |
| 135 | csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0); |
| 136 | icmph->checksum = csum_fold(csum); |
| 137 | |
| 138 | niph->tot_len = htons(nskb->len); |
| 139 | ip_send_check(niph); |
| 140 | |
| 141 | nft_reject_br_push_etherhdr(oldskb, nskb); |
| 142 | |
Nikolay Aleksandrov | 37b090e | 2016-07-14 06:10:02 +0300 | [diff] [blame] | 143 | br_forward(br_port_get_rcu(dev), nskb, false, true); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 144 | } |
| 145 | |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 146 | static void nft_reject_br_send_v6_tcp_reset(struct net *net, |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 147 | struct sk_buff *oldskb, |
| 148 | const struct net_device *dev, |
| 149 | int hook) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 150 | { |
| 151 | struct sk_buff *nskb; |
| 152 | const struct tcphdr *oth; |
| 153 | struct tcphdr _oth; |
| 154 | unsigned int otcplen; |
| 155 | struct ipv6hdr *nip6h; |
| 156 | |
Alvaro Neira | 68b0faa | 2014-11-26 10:21:36 +0100 | [diff] [blame] | 157 | if (!nft_bridge_ip6hdr_validate(oldskb)) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 158 | return; |
| 159 | |
| 160 | oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook); |
| 161 | if (!oth) |
| 162 | return; |
| 163 | |
| 164 | nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + |
| 165 | LL_MAX_HEADER, GFP_ATOMIC); |
| 166 | if (!nskb) |
| 167 | return; |
| 168 | |
| 169 | skb_reserve(nskb, LL_MAX_HEADER); |
| 170 | nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, |
| 171 | net->ipv6.devconf_all->hop_limit); |
| 172 | nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen); |
| 173 | nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); |
| 174 | |
| 175 | nft_reject_br_push_etherhdr(oldskb, nskb); |
| 176 | |
Nikolay Aleksandrov | 37b090e | 2016-07-14 06:10:02 +0300 | [diff] [blame] | 177 | br_forward(br_port_get_rcu(dev), nskb, false, true); |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | static bool reject6_br_csum_ok(struct sk_buff *skb, int hook) |
| 181 | { |
| 182 | const struct ipv6hdr *ip6h = ipv6_hdr(skb); |
| 183 | int thoff; |
| 184 | __be16 fo; |
| 185 | u8 proto = ip6h->nexthdr; |
| 186 | |
| 187 | if (skb->csum_bad) |
| 188 | return false; |
| 189 | |
| 190 | if (skb_csum_unnecessary(skb)) |
| 191 | return true; |
| 192 | |
| 193 | if (ip6h->payload_len && |
| 194 | pskb_trim_rcsum(skb, ntohs(ip6h->payload_len) + sizeof(*ip6h))) |
| 195 | return false; |
| 196 | |
| 197 | thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo); |
| 198 | if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) |
| 199 | return false; |
| 200 | |
| 201 | return nf_ip6_checksum(skb, hook, thoff, proto) == 0; |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | static void nft_reject_br_send_v6_unreach(struct net *net, |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 205 | struct sk_buff *oldskb, |
| 206 | const struct net_device *dev, |
| 207 | int hook, u8 code) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 208 | { |
| 209 | struct sk_buff *nskb; |
| 210 | struct ipv6hdr *nip6h; |
| 211 | struct icmp6hdr *icmp6h; |
| 212 | unsigned int len; |
| 213 | void *payload; |
| 214 | |
Alvaro Neira | 68b0faa | 2014-11-26 10:21:36 +0100 | [diff] [blame] | 215 | if (!nft_bridge_ip6hdr_validate(oldskb)) |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 216 | return; |
| 217 | |
| 218 | /* Include "As much of invoking packet as possible without the ICMPv6 |
| 219 | * packet exceeding the minimum IPv6 MTU" in the ICMP payload. |
| 220 | */ |
| 221 | len = min_t(unsigned int, 1220, oldskb->len); |
| 222 | |
| 223 | if (!pskb_may_pull(oldskb, len)) |
| 224 | return; |
| 225 | |
Florian Westphal | 72500bc | 2015-02-16 18:57:53 +0100 | [diff] [blame] | 226 | if (!reject6_br_csum_ok(oldskb, hook)) |
| 227 | return; |
| 228 | |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 229 | nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) + |
| 230 | LL_MAX_HEADER + len, GFP_ATOMIC); |
| 231 | if (!nskb) |
| 232 | return; |
| 233 | |
| 234 | skb_reserve(nskb, LL_MAX_HEADER); |
| 235 | nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6, |
| 236 | net->ipv6.devconf_all->hop_limit); |
| 237 | |
| 238 | skb_reset_transport_header(nskb); |
| 239 | icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr)); |
| 240 | memset(icmp6h, 0, sizeof(*icmp6h)); |
| 241 | icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; |
| 242 | icmp6h->icmp6_code = code; |
| 243 | |
| 244 | payload = skb_put(nskb, len); |
| 245 | memcpy(payload, skb_network_header(oldskb), len); |
| 246 | nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); |
| 247 | |
| 248 | icmp6h->icmp6_cksum = |
| 249 | csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, |
| 250 | nskb->len - sizeof(struct ipv6hdr), |
| 251 | IPPROTO_ICMPV6, |
| 252 | csum_partial(icmp6h, |
| 253 | nskb->len - sizeof(struct ipv6hdr), |
| 254 | 0)); |
| 255 | |
| 256 | nft_reject_br_push_etherhdr(oldskb, nskb); |
| 257 | |
Nikolay Aleksandrov | 37b090e | 2016-07-14 06:10:02 +0300 | [diff] [blame] | 258 | br_forward(br_port_get_rcu(dev), nskb, false, true); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 259 | } |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 260 | |
| 261 | static void nft_reject_bridge_eval(const struct nft_expr *expr, |
Patrick McHardy | a55e22e | 2015-04-11 02:27:31 +0100 | [diff] [blame] | 262 | struct nft_regs *regs, |
| 263 | const struct nft_pktinfo *pkt) |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 264 | { |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 265 | struct nft_reject *priv = nft_expr_priv(expr); |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 266 | const unsigned char *dest = eth_hdr(pkt->skb)->h_dest; |
| 267 | |
| 268 | if (is_broadcast_ether_addr(dest) || |
| 269 | is_multicast_ether_addr(dest)) |
| 270 | goto out; |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 271 | |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 272 | switch (eth_hdr(pkt->skb)->h_proto) { |
| 273 | case htons(ETH_P_IP): |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 274 | switch (priv->type) { |
| 275 | case NFT_REJECT_ICMP_UNREACH: |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 276 | nft_reject_br_send_v4_unreach(pkt->net, pkt->skb, |
| 277 | pkt->in, pkt->hook, |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 278 | priv->icmp_code); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 279 | break; |
| 280 | case NFT_REJECT_TCP_RST: |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 281 | nft_reject_br_send_v4_tcp_reset(pkt->net, pkt->skb, |
| 282 | pkt->in, pkt->hook); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 283 | break; |
| 284 | case NFT_REJECT_ICMPX_UNREACH: |
Liping Zhang | 2942119 | 2016-03-26 16:32:57 +0800 | [diff] [blame] | 285 | nft_reject_br_send_v4_unreach(pkt->net, pkt->skb, |
| 286 | pkt->in, pkt->hook, |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 287 | nft_reject_icmp_code(priv->icmp_code)); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 288 | break; |
| 289 | } |
| 290 | break; |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 291 | case htons(ETH_P_IPV6): |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 292 | switch (priv->type) { |
| 293 | case NFT_REJECT_ICMP_UNREACH: |
Eric W. Biederman | 88182a0 | 2015-09-18 14:33:01 -0500 | [diff] [blame] | 294 | nft_reject_br_send_v6_unreach(pkt->net, pkt->skb, |
| 295 | pkt->in, pkt->hook, |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 296 | priv->icmp_code); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 297 | break; |
| 298 | case NFT_REJECT_TCP_RST: |
Eric W. Biederman | 88182a0 | 2015-09-18 14:33:01 -0500 | [diff] [blame] | 299 | nft_reject_br_send_v6_tcp_reset(pkt->net, pkt->skb, |
| 300 | pkt->in, pkt->hook); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 301 | break; |
| 302 | case NFT_REJECT_ICMPX_UNREACH: |
Eric W. Biederman | 88182a0 | 2015-09-18 14:33:01 -0500 | [diff] [blame] | 303 | nft_reject_br_send_v6_unreach(pkt->net, pkt->skb, |
| 304 | pkt->in, pkt->hook, |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 305 | nft_reject_icmpv6_code(priv->icmp_code)); |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 306 | break; |
| 307 | } |
| 308 | break; |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 309 | default: |
| 310 | /* No explicit way to reject this protocol, drop it. */ |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 311 | break; |
| 312 | } |
Pablo Neira Ayuso | 523b929 | 2014-10-25 18:40:26 +0200 | [diff] [blame] | 313 | out: |
Patrick McHardy | a55e22e | 2015-04-11 02:27:31 +0100 | [diff] [blame] | 314 | regs->verdict.code = NF_DROP; |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 315 | } |
| 316 | |
Pablo Neira Ayuso | 75e8d06 | 2015-01-14 15:33:57 +0100 | [diff] [blame] | 317 | static int nft_reject_bridge_validate(const struct nft_ctx *ctx, |
| 318 | const struct nft_expr *expr, |
| 319 | const struct nft_data **data) |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 320 | { |
Pablo Neira Ayuso | 75e8d06 | 2015-01-14 15:33:57 +0100 | [diff] [blame] | 321 | return nft_chain_validate_hooks(ctx->chain, (1 << NF_BR_PRE_ROUTING) | |
| 322 | (1 << NF_BR_LOCAL_IN)); |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 323 | } |
| 324 | |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 325 | static int nft_reject_bridge_init(const struct nft_ctx *ctx, |
| 326 | const struct nft_expr *expr, |
| 327 | const struct nlattr * const tb[]) |
| 328 | { |
| 329 | struct nft_reject *priv = nft_expr_priv(expr); |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 330 | int icmp_code, err; |
| 331 | |
Pablo Neira Ayuso | 75e8d06 | 2015-01-14 15:33:57 +0100 | [diff] [blame] | 332 | err = nft_reject_bridge_validate(ctx, expr, NULL); |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 333 | if (err < 0) |
| 334 | return err; |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 335 | |
| 336 | if (tb[NFTA_REJECT_TYPE] == NULL) |
| 337 | return -EINVAL; |
| 338 | |
| 339 | priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE])); |
| 340 | switch (priv->type) { |
| 341 | case NFT_REJECT_ICMP_UNREACH: |
| 342 | case NFT_REJECT_ICMPX_UNREACH: |
| 343 | if (tb[NFTA_REJECT_ICMP_CODE] == NULL) |
| 344 | return -EINVAL; |
| 345 | |
| 346 | icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]); |
| 347 | if (priv->type == NFT_REJECT_ICMPX_UNREACH && |
| 348 | icmp_code > NFT_REJECT_ICMPX_MAX) |
| 349 | return -EINVAL; |
| 350 | |
| 351 | priv->icmp_code = icmp_code; |
| 352 | break; |
| 353 | case NFT_REJECT_TCP_RST: |
| 354 | break; |
| 355 | default: |
| 356 | return -EINVAL; |
| 357 | } |
| 358 | return 0; |
| 359 | } |
| 360 | |
| 361 | static int nft_reject_bridge_dump(struct sk_buff *skb, |
| 362 | const struct nft_expr *expr) |
| 363 | { |
| 364 | const struct nft_reject *priv = nft_expr_priv(expr); |
| 365 | |
| 366 | if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type))) |
| 367 | goto nla_put_failure; |
| 368 | |
| 369 | switch (priv->type) { |
| 370 | case NFT_REJECT_ICMP_UNREACH: |
| 371 | case NFT_REJECT_ICMPX_UNREACH: |
| 372 | if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code)) |
| 373 | goto nla_put_failure; |
| 374 | break; |
David Miller | c1f8667 | 2015-04-07 23:05:42 -0400 | [diff] [blame] | 375 | default: |
| 376 | break; |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 377 | } |
| 378 | |
| 379 | return 0; |
| 380 | |
| 381 | nla_put_failure: |
| 382 | return -1; |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 383 | } |
| 384 | |
| 385 | static struct nft_expr_type nft_reject_bridge_type; |
| 386 | static const struct nft_expr_ops nft_reject_bridge_ops = { |
| 387 | .type = &nft_reject_bridge_type, |
| 388 | .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), |
| 389 | .eval = nft_reject_bridge_eval, |
Pablo Neira Ayuso | 51b0a5d | 2014-09-26 14:35:14 +0200 | [diff] [blame] | 390 | .init = nft_reject_bridge_init, |
| 391 | .dump = nft_reject_bridge_dump, |
Pablo Neira Ayuso | 127917c | 2014-10-27 14:08:17 +0100 | [diff] [blame] | 392 | .validate = nft_reject_bridge_validate, |
Pablo Neira Ayuso | 85f5b30 | 2014-06-27 13:36:11 +0200 | [diff] [blame] | 393 | }; |
| 394 | |
| 395 | static struct nft_expr_type nft_reject_bridge_type __read_mostly = { |
| 396 | .family = NFPROTO_BRIDGE, |
| 397 | .name = "reject", |
| 398 | .ops = &nft_reject_bridge_ops, |
| 399 | .policy = nft_reject_policy, |
| 400 | .maxattr = NFTA_REJECT_MAX, |
| 401 | .owner = THIS_MODULE, |
| 402 | }; |
| 403 | |
| 404 | static int __init nft_reject_bridge_module_init(void) |
| 405 | { |
| 406 | return nft_register_expr(&nft_reject_bridge_type); |
| 407 | } |
| 408 | |
| 409 | static void __exit nft_reject_bridge_module_exit(void) |
| 410 | { |
| 411 | nft_unregister_expr(&nft_reject_bridge_type); |
| 412 | } |
| 413 | |
| 414 | module_init(nft_reject_bridge_module_init); |
| 415 | module_exit(nft_reject_bridge_module_exit); |
| 416 | |
| 417 | MODULE_LICENSE("GPL"); |
| 418 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); |
| 419 | MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject"); |