Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2020 Laura Garcia Liebana <nevola@gmail.com>
0004  * Copyright (c) 2020 Jose M. Guisado <guigom@riseup.net>
0005  */
0006 
0007 #include <linux/etherdevice.h>
0008 #include <linux/kernel.h>
0009 #include <linux/init.h>
0010 #include <linux/module.h>
0011 #include <linux/netlink.h>
0012 #include <linux/netfilter.h>
0013 #include <linux/netfilter/nf_tables.h>
0014 #include <net/netfilter/nf_tables.h>
0015 #include <net/netfilter/nft_reject.h>
0016 #include <net/netfilter/ipv4/nf_reject.h>
0017 #include <net/netfilter/ipv6/nf_reject.h>
0018 
0019 static void nft_reject_queue_xmit(struct sk_buff *nskb, struct sk_buff *oldskb)
0020 {
0021     dev_hard_header(nskb, nskb->dev, ntohs(oldskb->protocol),
0022             eth_hdr(oldskb)->h_source, eth_hdr(oldskb)->h_dest,
0023             nskb->len);
0024     dev_queue_xmit(nskb);
0025 }
0026 
0027 static void nft_reject_netdev_send_v4_tcp_reset(struct net *net,
0028                         struct sk_buff *oldskb,
0029                         const struct net_device *dev,
0030                         int hook)
0031 {
0032     struct sk_buff *nskb;
0033 
0034     nskb = nf_reject_skb_v4_tcp_reset(net, oldskb, dev, hook);
0035     if (!nskb)
0036         return;
0037 
0038     nft_reject_queue_xmit(nskb, oldskb);
0039 }
0040 
0041 static void nft_reject_netdev_send_v4_unreach(struct net *net,
0042                           struct sk_buff *oldskb,
0043                           const struct net_device *dev,
0044                           int hook, u8 code)
0045 {
0046     struct sk_buff *nskb;
0047 
0048     nskb = nf_reject_skb_v4_unreach(net, oldskb, dev, hook, code);
0049     if (!nskb)
0050         return;
0051 
0052     nft_reject_queue_xmit(nskb, oldskb);
0053 }
0054 
0055 static void nft_reject_netdev_send_v6_tcp_reset(struct net *net,
0056                         struct sk_buff *oldskb,
0057                         const struct net_device *dev,
0058                         int hook)
0059 {
0060     struct sk_buff *nskb;
0061 
0062     nskb = nf_reject_skb_v6_tcp_reset(net, oldskb, dev, hook);
0063     if (!nskb)
0064         return;
0065 
0066     nft_reject_queue_xmit(nskb, oldskb);
0067 }
0068 
0069 
0070 static void nft_reject_netdev_send_v6_unreach(struct net *net,
0071                           struct sk_buff *oldskb,
0072                           const struct net_device *dev,
0073                           int hook, u8 code)
0074 {
0075     struct sk_buff *nskb;
0076 
0077     nskb = nf_reject_skb_v6_unreach(net, oldskb, dev, hook, code);
0078     if (!nskb)
0079         return;
0080 
0081     nft_reject_queue_xmit(nskb, oldskb);
0082 }
0083 
0084 static void nft_reject_netdev_eval(const struct nft_expr *expr,
0085                    struct nft_regs *regs,
0086                    const struct nft_pktinfo *pkt)
0087 {
0088     struct ethhdr *eth = eth_hdr(pkt->skb);
0089     struct nft_reject *priv = nft_expr_priv(expr);
0090     const unsigned char *dest = eth->h_dest;
0091 
0092     if (is_broadcast_ether_addr(dest) ||
0093         is_multicast_ether_addr(dest))
0094         goto out;
0095 
0096     switch (eth->h_proto) {
0097     case htons(ETH_P_IP):
0098         switch (priv->type) {
0099         case NFT_REJECT_ICMP_UNREACH:
0100             nft_reject_netdev_send_v4_unreach(nft_net(pkt), pkt->skb,
0101                               nft_in(pkt),
0102                               nft_hook(pkt),
0103                               priv->icmp_code);
0104             break;
0105         case NFT_REJECT_TCP_RST:
0106             nft_reject_netdev_send_v4_tcp_reset(nft_net(pkt), pkt->skb,
0107                                 nft_in(pkt),
0108                                 nft_hook(pkt));
0109             break;
0110         case NFT_REJECT_ICMPX_UNREACH:
0111             nft_reject_netdev_send_v4_unreach(nft_net(pkt), pkt->skb,
0112                               nft_in(pkt),
0113                               nft_hook(pkt),
0114                               nft_reject_icmp_code(priv->icmp_code));
0115             break;
0116         }
0117         break;
0118     case htons(ETH_P_IPV6):
0119         switch (priv->type) {
0120         case NFT_REJECT_ICMP_UNREACH:
0121             nft_reject_netdev_send_v6_unreach(nft_net(pkt), pkt->skb,
0122                               nft_in(pkt),
0123                               nft_hook(pkt),
0124                               priv->icmp_code);
0125             break;
0126         case NFT_REJECT_TCP_RST:
0127             nft_reject_netdev_send_v6_tcp_reset(nft_net(pkt), pkt->skb,
0128                                 nft_in(pkt),
0129                                 nft_hook(pkt));
0130             break;
0131         case NFT_REJECT_ICMPX_UNREACH:
0132             nft_reject_netdev_send_v6_unreach(nft_net(pkt), pkt->skb,
0133                               nft_in(pkt),
0134                               nft_hook(pkt),
0135                               nft_reject_icmpv6_code(priv->icmp_code));
0136             break;
0137         }
0138         break;
0139     default:
0140         /* No explicit way to reject this protocol, drop it. */
0141         break;
0142     }
0143 out:
0144     regs->verdict.code = NF_DROP;
0145 }
0146 
0147 static int nft_reject_netdev_validate(const struct nft_ctx *ctx,
0148                       const struct nft_expr *expr,
0149                       const struct nft_data **data)
0150 {
0151     return nft_chain_validate_hooks(ctx->chain, (1 << NF_NETDEV_INGRESS));
0152 }
0153 
0154 static struct nft_expr_type nft_reject_netdev_type;
0155 static const struct nft_expr_ops nft_reject_netdev_ops = {
0156     .type       = &nft_reject_netdev_type,
0157     .size       = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
0158     .eval       = nft_reject_netdev_eval,
0159     .init       = nft_reject_init,
0160     .dump       = nft_reject_dump,
0161     .validate   = nft_reject_netdev_validate,
0162     .reduce     = NFT_REDUCE_READONLY,
0163 };
0164 
0165 static struct nft_expr_type nft_reject_netdev_type __read_mostly = {
0166     .family     = NFPROTO_NETDEV,
0167     .name       = "reject",
0168     .ops        = &nft_reject_netdev_ops,
0169     .policy     = nft_reject_policy,
0170     .maxattr    = NFTA_REJECT_MAX,
0171     .owner      = THIS_MODULE,
0172 };
0173 
0174 static int __init nft_reject_netdev_module_init(void)
0175 {
0176     return nft_register_expr(&nft_reject_netdev_type);
0177 }
0178 
0179 static void __exit nft_reject_netdev_module_exit(void)
0180 {
0181     nft_unregister_expr(&nft_reject_netdev_type);
0182 }
0183 
0184 module_init(nft_reject_netdev_module_init);
0185 module_exit(nft_reject_netdev_module_exit);
0186 
0187 MODULE_LICENSE("GPL");
0188 MODULE_AUTHOR("Laura Garcia Liebana <nevola@gmail.com>");
0189 MODULE_AUTHOR("Jose M. Guisado <guigom@riseup.net>");
0190 MODULE_DESCRIPTION("Reject packets from netdev via nftables");
0191 MODULE_ALIAS_NFT_AF_EXPR(5, "reject");