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