Skip to content

Commit 51b0a5d

Browse files
committed
netfilter: nft_reject: introduce icmp code abstraction for inet and bridge
This patch introduces the NFT_REJECT_ICMPX_UNREACH type which provides an abstraction to the ICMP and ICMPv6 codes that you can use from the inet and bridge tables, they are: * NFT_REJECT_ICMPX_NO_ROUTE: no route to host - network unreachable * NFT_REJECT_ICMPX_PORT_UNREACH: port unreachable * NFT_REJECT_ICMPX_HOST_UNREACH: host unreachable * NFT_REJECT_ICMPX_ADMIN_PROHIBITED: administratevely prohibited You can still use the specific codes when restricting the rule to match the corresponding layer 3 protocol. I decided to not overload the existing NFT_REJECT_ICMP_UNREACH to have different semantics depending on the table family and to allow the user to specify ICMP family specific codes if they restrict it to the corresponding family. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 2c804d0 commit 51b0a5d

File tree

7 files changed

+241
-17
lines changed

7 files changed

+241
-17
lines changed

include/net/netfilter/ipv4/nf_reject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <net/tcp.h>
66
#include <net/route.h>
77
#include <net/dst.h>
8+
#include <net/icmp.h>
89

910
static inline void nf_send_unreach(struct sk_buff *skb_in, int code)
1011
{

include/net/netfilter/nft_reject.h

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ int nft_reject_init(const struct nft_ctx *ctx,
1414

1515
int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr);
1616

17-
void nft_reject_ipv4_eval(const struct nft_expr *expr,
18-
struct nft_data data[NFT_REG_MAX + 1],
19-
const struct nft_pktinfo *pkt);
20-
21-
void nft_reject_ipv6_eval(const struct nft_expr *expr,
22-
struct nft_data data[NFT_REG_MAX + 1],
23-
const struct nft_pktinfo *pkt);
17+
int nft_reject_icmp_code(u8 code);
18+
int nft_reject_icmpv6_code(u8 code);
2419

2520
#endif

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,12 +749,33 @@ enum nft_queue_attributes {
749749
*
750750
* @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable
751751
* @NFT_REJECT_TCP_RST: reject using TCP RST
752+
* @NFT_REJECT_ICMPX_UNREACH: abstracted ICMP unreachable for bridge and inet
752753
*/
753754
enum nft_reject_types {
754755
NFT_REJECT_ICMP_UNREACH,
755756
NFT_REJECT_TCP_RST,
757+
NFT_REJECT_ICMPX_UNREACH,
756758
};
757759

760+
/**
761+
* enum nft_reject_code - Generic reject codes for IPv4/IPv6
762+
*
763+
* @NFT_REJECT_ICMPX_NO_ROUTE: no route to host / network unreachable
764+
* @NFT_REJECT_ICMPX_PORT_UNREACH: port unreachable
765+
* @NFT_REJECT_ICMPX_HOST_UNREACH: host unreachable
766+
* @NFT_REJECT_ICMPX_ADMIN_PROHIBITED: administratively prohibited
767+
*
768+
* These codes are mapped to real ICMP and ICMPv6 codes.
769+
*/
770+
enum nft_reject_inet_code {
771+
NFT_REJECT_ICMPX_NO_ROUTE = 0,
772+
NFT_REJECT_ICMPX_PORT_UNREACH,
773+
NFT_REJECT_ICMPX_HOST_UNREACH,
774+
NFT_REJECT_ICMPX_ADMIN_PROHIBITED,
775+
__NFT_REJECT_ICMPX_MAX
776+
};
777+
#define NFT_REJECT_ICMPX_MAX (__NFT_REJECT_ICMPX_MAX + 1)
778+
758779
/**
759780
* enum nft_reject_attributes - nf_tables reject expression netlink attributes
760781
*

net/bridge/netfilter/nft_reject_bridge.c

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,115 @@
1414
#include <linux/netfilter/nf_tables.h>
1515
#include <net/netfilter/nf_tables.h>
1616
#include <net/netfilter/nft_reject.h>
17+
#include <net/netfilter/ipv4/nf_reject.h>
18+
#include <net/netfilter/ipv6/nf_reject.h>
1719

1820
static void nft_reject_bridge_eval(const struct nft_expr *expr,
1921
struct nft_data data[NFT_REG_MAX + 1],
2022
const struct nft_pktinfo *pkt)
2123
{
24+
struct nft_reject *priv = nft_expr_priv(expr);
25+
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
26+
2227
switch (eth_hdr(pkt->skb)->h_proto) {
2328
case htons(ETH_P_IP):
24-
return nft_reject_ipv4_eval(expr, data, pkt);
29+
switch (priv->type) {
30+
case NFT_REJECT_ICMP_UNREACH:
31+
nf_send_unreach(pkt->skb, priv->icmp_code);
32+
break;
33+
case NFT_REJECT_TCP_RST:
34+
nf_send_reset(pkt->skb, pkt->ops->hooknum);
35+
break;
36+
case NFT_REJECT_ICMPX_UNREACH:
37+
nf_send_unreach(pkt->skb,
38+
nft_reject_icmp_code(priv->icmp_code));
39+
break;
40+
}
41+
break;
2542
case htons(ETH_P_IPV6):
26-
return nft_reject_ipv6_eval(expr, data, pkt);
43+
switch (priv->type) {
44+
case NFT_REJECT_ICMP_UNREACH:
45+
nf_send_unreach6(net, pkt->skb, priv->icmp_code,
46+
pkt->ops->hooknum);
47+
break;
48+
case NFT_REJECT_TCP_RST:
49+
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
50+
break;
51+
case NFT_REJECT_ICMPX_UNREACH:
52+
nf_send_unreach6(net, pkt->skb,
53+
nft_reject_icmpv6_code(priv->icmp_code),
54+
pkt->ops->hooknum);
55+
break;
56+
}
57+
break;
2758
default:
2859
/* No explicit way to reject this protocol, drop it. */
29-
data[NFT_REG_VERDICT].verdict = NF_DROP;
3060
break;
3161
}
62+
data[NFT_REG_VERDICT].verdict = NF_DROP;
63+
}
64+
65+
static int nft_reject_bridge_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+
97+
static int nft_reject_bridge_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+
}
112+
113+
return 0;
114+
115+
nla_put_failure:
116+
return -1;
32117
}
33118

34119
static struct nft_expr_type nft_reject_bridge_type;
35120
static const struct nft_expr_ops nft_reject_bridge_ops = {
36121
.type = &nft_reject_bridge_type,
37122
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
38123
.eval = nft_reject_bridge_eval,
39-
.init = nft_reject_init,
40-
.dump = nft_reject_dump,
124+
.init = nft_reject_bridge_init,
125+
.dump = nft_reject_bridge_dump,
41126
};
42127

43128
static struct nft_expr_type nft_reject_bridge_type __read_mostly = {

net/ipv4/netfilter/nft_reject_ipv4.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include <linux/netfilter.h>
1717
#include <linux/netfilter/nf_tables.h>
1818
#include <net/netfilter/nf_tables.h>
19-
#include <net/icmp.h>
2019
#include <net/netfilter/ipv4/nf_reject.h>
2120
#include <net/netfilter/nft_reject.h>
2221

net/netfilter/nft_reject.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <linux/netfilter/nf_tables.h>
1818
#include <net/netfilter/nf_tables.h>
1919
#include <net/netfilter/nft_reject.h>
20+
#include <linux/icmp.h>
21+
#include <linux/icmpv6.h>
2022

2123
const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = {
2224
[NFTA_REJECT_TYPE] = { .type = NLA_U32 },
@@ -70,5 +72,40 @@ int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr)
7072
}
7173
EXPORT_SYMBOL_GPL(nft_reject_dump);
7274

75+
static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX] = {
76+
[NFT_REJECT_ICMPX_NO_ROUTE] = ICMP_NET_UNREACH,
77+
[NFT_REJECT_ICMPX_PORT_UNREACH] = ICMP_PORT_UNREACH,
78+
[NFT_REJECT_ICMPX_HOST_UNREACH] = ICMP_HOST_UNREACH,
79+
[NFT_REJECT_ICMPX_ADMIN_PROHIBITED] = ICMP_PKT_FILTERED,
80+
};
81+
82+
int nft_reject_icmp_code(u8 code)
83+
{
84+
if (code > NFT_REJECT_ICMPX_MAX)
85+
return -EINVAL;
86+
87+
return icmp_code_v4[code];
88+
}
89+
90+
EXPORT_SYMBOL_GPL(nft_reject_icmp_code);
91+
92+
93+
static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX] = {
94+
[NFT_REJECT_ICMPX_NO_ROUTE] = ICMPV6_NOROUTE,
95+
[NFT_REJECT_ICMPX_PORT_UNREACH] = ICMPV6_PORT_UNREACH,
96+
[NFT_REJECT_ICMPX_HOST_UNREACH] = ICMPV6_ADDR_UNREACH,
97+
[NFT_REJECT_ICMPX_ADMIN_PROHIBITED] = ICMPV6_ADM_PROHIBITED,
98+
};
99+
100+
int nft_reject_icmpv6_code(u8 code)
101+
{
102+
if (code > NFT_REJECT_ICMPX_MAX)
103+
return -EINVAL;
104+
105+
return icmp_code_v6[code];
106+
}
107+
108+
EXPORT_SYMBOL_GPL(nft_reject_icmpv6_code);
109+
73110
MODULE_LICENSE("GPL");
74111
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");

net/netfilter/nft_reject_inet.c

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,112 @@
1414
#include <linux/netfilter/nf_tables.h>
1515
#include <net/netfilter/nf_tables.h>
1616
#include <net/netfilter/nft_reject.h>
17+
#include <net/netfilter/ipv4/nf_reject.h>
18+
#include <net/netfilter/ipv6/nf_reject.h>
1719

1820
static void nft_reject_inet_eval(const struct nft_expr *expr,
1921
struct nft_data data[NFT_REG_MAX + 1],
2022
const struct nft_pktinfo *pkt)
2123
{
24+
struct nft_reject *priv = nft_expr_priv(expr);
25+
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
26+
2227
switch (pkt->ops->pf) {
2328
case NFPROTO_IPV4:
24-
return nft_reject_ipv4_eval(expr, data, pkt);
29+
switch (priv->type) {
30+
case NFT_REJECT_ICMP_UNREACH:
31+
nf_send_unreach(pkt->skb, priv->icmp_code);
32+
break;
33+
case NFT_REJECT_TCP_RST:
34+
nf_send_reset(pkt->skb, pkt->ops->hooknum);
35+
break;
36+
case NFT_REJECT_ICMPX_UNREACH:
37+
nf_send_unreach(pkt->skb,
38+
nft_reject_icmp_code(priv->icmp_code));
39+
break;
40+
}
41+
break;
2542
case NFPROTO_IPV6:
26-
return nft_reject_ipv6_eval(expr, data, pkt);
43+
switch (priv->type) {
44+
case NFT_REJECT_ICMP_UNREACH:
45+
nf_send_unreach6(net, pkt->skb, priv->icmp_code,
46+
pkt->ops->hooknum);
47+
break;
48+
case NFT_REJECT_TCP_RST:
49+
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
50+
break;
51+
case NFT_REJECT_ICMPX_UNREACH:
52+
nf_send_unreach6(net, pkt->skb,
53+
nft_reject_icmpv6_code(priv->icmp_code),
54+
pkt->ops->hooknum);
55+
break;
56+
}
57+
break;
58+
}
59+
data[NFT_REG_VERDICT].verdict = NF_DROP;
60+
}
61+
62+
static int nft_reject_inet_init(const struct nft_ctx *ctx,
63+
const struct nft_expr *expr,
64+
const struct nlattr * const tb[])
65+
{
66+
struct nft_reject *priv = nft_expr_priv(expr);
67+
int icmp_code;
68+
69+
if (tb[NFTA_REJECT_TYPE] == NULL)
70+
return -EINVAL;
71+
72+
priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
73+
switch (priv->type) {
74+
case NFT_REJECT_ICMP_UNREACH:
75+
case NFT_REJECT_ICMPX_UNREACH:
76+
if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
77+
return -EINVAL;
78+
79+
icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
80+
if (priv->type == NFT_REJECT_ICMPX_UNREACH &&
81+
icmp_code > NFT_REJECT_ICMPX_MAX)
82+
return -EINVAL;
83+
84+
priv->icmp_code = icmp_code;
85+
break;
86+
case NFT_REJECT_TCP_RST:
87+
break;
88+
default:
89+
return -EINVAL;
2790
}
91+
return 0;
92+
}
93+
94+
static int nft_reject_inet_dump(struct sk_buff *skb,
95+
const struct nft_expr *expr)
96+
{
97+
const struct nft_reject *priv = nft_expr_priv(expr);
98+
99+
if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type)))
100+
goto nla_put_failure;
101+
102+
switch (priv->type) {
103+
case NFT_REJECT_ICMP_UNREACH:
104+
case NFT_REJECT_ICMPX_UNREACH:
105+
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
106+
goto nla_put_failure;
107+
break;
108+
}
109+
110+
return 0;
111+
112+
nla_put_failure:
113+
return -1;
28114
}
29115

30116
static struct nft_expr_type nft_reject_inet_type;
31117
static const struct nft_expr_ops nft_reject_inet_ops = {
32118
.type = &nft_reject_inet_type,
33119
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
34120
.eval = nft_reject_inet_eval,
35-
.init = nft_reject_init,
36-
.dump = nft_reject_dump,
121+
.init = nft_reject_inet_init,
122+
.dump = nft_reject_inet_dump,
37123
};
38124

39125
static struct nft_expr_type nft_reject_inet_type __read_mostly = {

0 commit comments

Comments
 (0)