1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014 Patrick McHardy <kaber@trash.net> 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 17 static void nft_reject_inet_eval(const struct nft_expr *expr, 18 struct nft_regs *regs, 19 const struct nft_pktinfo *pkt) 20 { 21 struct nft_reject *priv = nft_expr_priv(expr); 22 23 switch (nft_pf(pkt)) { 24 case NFPROTO_IPV4: 25 switch (priv->type) { 26 case NFT_REJECT_ICMP_UNREACH: 27 nf_send_unreach(pkt->skb, priv->icmp_code, 28 nft_hook(pkt)); 29 break; 30 case NFT_REJECT_TCP_RST: 31 nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt)); 32 break; 33 case NFT_REJECT_ICMPX_UNREACH: 34 nf_send_unreach(pkt->skb, 35 nft_reject_icmp_code(priv->icmp_code), 36 nft_hook(pkt)); 37 break; 38 } 39 break; 40 case NFPROTO_IPV6: 41 switch (priv->type) { 42 case NFT_REJECT_ICMP_UNREACH: 43 nf_send_unreach6(nft_net(pkt), pkt->skb, 44 priv->icmp_code, nft_hook(pkt)); 45 break; 46 case NFT_REJECT_TCP_RST: 47 nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt)); 48 break; 49 case NFT_REJECT_ICMPX_UNREACH: 50 nf_send_unreach6(nft_net(pkt), pkt->skb, 51 nft_reject_icmpv6_code(priv->icmp_code), 52 nft_hook(pkt)); 53 break; 54 } 55 break; 56 } 57 58 regs->verdict.code = NF_DROP; 59 } 60 61 static int nft_reject_inet_init(const struct nft_ctx *ctx, 62 const struct nft_expr *expr, 63 const struct nlattr * const tb[]) 64 { 65 struct nft_reject *priv = nft_expr_priv(expr); 66 int icmp_code; 67 68 if (tb[NFTA_REJECT_TYPE] == NULL) 69 return -EINVAL; 70 71 priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE])); 72 switch (priv->type) { 73 case NFT_REJECT_ICMP_UNREACH: 74 case NFT_REJECT_ICMPX_UNREACH: 75 if (tb[NFTA_REJECT_ICMP_CODE] == NULL) 76 return -EINVAL; 77 78 icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]); 79 if (priv->type == NFT_REJECT_ICMPX_UNREACH && 80 icmp_code > NFT_REJECT_ICMPX_MAX) 81 return -EINVAL; 82 83 priv->icmp_code = icmp_code; 84 break; 85 case NFT_REJECT_TCP_RST: 86 break; 87 default: 88 return -EINVAL; 89 } 90 return 0; 91 } 92 93 static int nft_reject_inet_dump(struct sk_buff *skb, 94 const struct nft_expr *expr) 95 { 96 const struct nft_reject *priv = nft_expr_priv(expr); 97 98 if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type))) 99 goto nla_put_failure; 100 101 switch (priv->type) { 102 case NFT_REJECT_ICMP_UNREACH: 103 case NFT_REJECT_ICMPX_UNREACH: 104 if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code)) 105 goto nla_put_failure; 106 break; 107 default: 108 break; 109 } 110 111 return 0; 112 113 nla_put_failure: 114 return -1; 115 } 116 117 static struct nft_expr_type nft_reject_inet_type; 118 static const struct nft_expr_ops nft_reject_inet_ops = { 119 .type = &nft_reject_inet_type, 120 .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), 121 .eval = nft_reject_inet_eval, 122 .init = nft_reject_inet_init, 123 .dump = nft_reject_inet_dump, 124 .validate = nft_reject_validate, 125 }; 126 127 static struct nft_expr_type nft_reject_inet_type __read_mostly = { 128 .family = NFPROTO_INET, 129 .name = "reject", 130 .ops = &nft_reject_inet_ops, 131 .policy = nft_reject_policy, 132 .maxattr = NFTA_REJECT_MAX, 133 .owner = THIS_MODULE, 134 }; 135 136 static int __init nft_reject_inet_module_init(void) 137 { 138 return nft_register_expr(&nft_reject_inet_type); 139 } 140 141 static void __exit nft_reject_inet_module_exit(void) 142 { 143 nft_unregister_expr(&nft_reject_inet_type); 144 } 145 146 module_init(nft_reject_inet_module_init); 147 module_exit(nft_reject_inet_module_exit); 148 149 MODULE_LICENSE("GPL"); 150 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 151 MODULE_ALIAS_NFT_AF_EXPR(1, "reject"); 152 MODULE_DESCRIPTION("Netfilter nftables reject inet support"); 153