linux/net/netfilter/nft_reject_netdev.c
<<
>>
Prefs
   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
  18static 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
  26static 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
  40static 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
  54static 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
  69static 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
  83static 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        }
 142out:
 143        regs->verdict.code = NF_DROP;
 144}
 145
 146static 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
 153static struct nft_expr_type nft_reject_netdev_type;
 154static 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
 163static 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
 172static int __init nft_reject_netdev_module_init(void)
 173{
 174        return nft_register_expr(&nft_reject_netdev_type);
 175}
 176
 177static void __exit nft_reject_netdev_module_exit(void)
 178{
 179        nft_unregister_expr(&nft_reject_netdev_type);
 180}
 181
 182module_init(nft_reject_netdev_module_init);
 183module_exit(nft_reject_netdev_module_exit);
 184
 185MODULE_LICENSE("GPL");
 186MODULE_AUTHOR("Laura Garcia Liebana <nevola@gmail.com>");
 187MODULE_AUTHOR("Jose M. Guisado <guigom@riseup.net>");
 188MODULE_DESCRIPTION("Reject packets from netdev via nftables");
 189MODULE_ALIAS_NFT_AF_EXPR(5, "reject");
 190