linux/net/bridge/netfilter/nft_reject_bridge.c
<<
>>
Prefs
   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
  23static 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 */
  45static 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
  61static 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
  77static 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
  94static 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
 110static 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        }
 168out:
 169        regs->verdict.code = NF_DROP;
 170}
 171
 172static 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
 180static struct nft_expr_type nft_reject_bridge_type;
 181static 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
 190static 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
 199static int __init nft_reject_bridge_module_init(void)
 200{
 201        return nft_register_expr(&nft_reject_bridge_type);
 202}
 203
 204static void __exit nft_reject_bridge_module_exit(void)
 205{
 206        nft_unregister_expr(&nft_reject_bridge_type);
 207}
 208
 209module_init(nft_reject_bridge_module_init);
 210module_exit(nft_reject_bridge_module_exit);
 211
 212MODULE_LICENSE("GPL");
 213MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 214MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject");
 215MODULE_DESCRIPTION("Reject packets from bridge via nftables");
 216