diff options
-rw-r--r-- | include/net/netfilter/ipv4/nf_reject.h | 1 | ||||
-rw-r--r-- | include/net/netfilter/nft_reject.h | 9 | ||||
-rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 21 | ||||
-rw-r--r-- | net/bridge/netfilter/nft_reject_bridge.c | 95 | ||||
-rw-r--r-- | net/ipv4/netfilter/nft_reject_ipv4.c | 1 | ||||
-rw-r--r-- | net/netfilter/nft_reject.c | 37 | ||||
-rw-r--r-- | net/netfilter/nft_reject_inet.c | 94 |
7 files changed, 241 insertions, 17 deletions
diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h index f713b5a31d62..8ce06385a552 100644 --- a/include/net/netfilter/ipv4/nf_reject.h +++ b/include/net/netfilter/ipv4/nf_reject.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <net/tcp.h> | 5 | #include <net/tcp.h> |
6 | #include <net/route.h> | 6 | #include <net/route.h> |
7 | #include <net/dst.h> | 7 | #include <net/dst.h> |
8 | #include <net/icmp.h> | ||
8 | 9 | ||
9 | static inline void nf_send_unreach(struct sk_buff *skb_in, int code) | 10 | static inline void nf_send_unreach(struct sk_buff *skb_in, int code) |
10 | { | 11 | { |
diff --git a/include/net/netfilter/nft_reject.h b/include/net/netfilter/nft_reject.h index 36b0da2d55bb..60fa1530006b 100644 --- a/include/net/netfilter/nft_reject.h +++ b/include/net/netfilter/nft_reject.h | |||
@@ -14,12 +14,7 @@ int nft_reject_init(const struct nft_ctx *ctx, | |||
14 | 14 | ||
15 | int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr); | 15 | int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr); |
16 | 16 | ||
17 | void nft_reject_ipv4_eval(const struct nft_expr *expr, | 17 | int nft_reject_icmp_code(u8 code); |
18 | struct nft_data data[NFT_REG_MAX + 1], | 18 | int nft_reject_icmpv6_code(u8 code); |
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); | ||
24 | 19 | ||
25 | #endif | 20 | #endif |
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index b72ccfeaf865..c26df6787fb0 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
@@ -749,13 +749,34 @@ enum nft_queue_attributes { | |||
749 | * | 749 | * |
750 | * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable | 750 | * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable |
751 | * @NFT_REJECT_TCP_RST: reject using TCP RST | 751 | * @NFT_REJECT_TCP_RST: reject using TCP RST |
752 | * @NFT_REJECT_ICMPX_UNREACH: abstracted ICMP unreachable for bridge and inet | ||
752 | */ | 753 | */ |
753 | enum nft_reject_types { | 754 | enum nft_reject_types { |
754 | NFT_REJECT_ICMP_UNREACH, | 755 | NFT_REJECT_ICMP_UNREACH, |
755 | NFT_REJECT_TCP_RST, | 756 | NFT_REJECT_TCP_RST, |
757 | NFT_REJECT_ICMPX_UNREACH, | ||
756 | }; | 758 | }; |
757 | 759 | ||
758 | /** | 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 | |||
779 | /** | ||
759 | * enum nft_reject_attributes - nf_tables reject expression netlink attributes | 780 | * enum nft_reject_attributes - nf_tables reject expression netlink attributes |
760 | * | 781 | * |
761 | * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types) | 782 | * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types) |
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index ee3ffe93e14e..a76479535df2 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c | |||
@@ -14,21 +14,106 @@ | |||
14 | #include <linux/netfilter/nf_tables.h> | 14 | #include <linux/netfilter/nf_tables.h> |
15 | #include <net/netfilter/nf_tables.h> | 15 | #include <net/netfilter/nf_tables.h> |
16 | #include <net/netfilter/nft_reject.h> | 16 | #include <net/netfilter/nft_reject.h> |
17 | #include <net/netfilter/ipv4/nf_reject.h> | ||
18 | #include <net/netfilter/ipv6/nf_reject.h> | ||
17 | 19 | ||
18 | static void nft_reject_bridge_eval(const struct nft_expr *expr, | 20 | static void nft_reject_bridge_eval(const struct nft_expr *expr, |
19 | struct nft_data data[NFT_REG_MAX + 1], | 21 | struct nft_data data[NFT_REG_MAX + 1], |
20 | const struct nft_pktinfo *pkt) | 22 | const struct nft_pktinfo *pkt) |
21 | { | 23 | { |
24 | struct nft_reject *priv = nft_expr_priv(expr); | ||
25 | struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); | ||
26 | |||
22 | switch (eth_hdr(pkt->skb)->h_proto) { | 27 | switch (eth_hdr(pkt->skb)->h_proto) { |
23 | case htons(ETH_P_IP): | 28 | 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; | ||
25 | case htons(ETH_P_IPV6): | 42 | 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; | ||
27 | default: | 58 | default: |
28 | /* No explicit way to reject this protocol, drop it. */ | 59 | /* No explicit way to reject this protocol, drop it. */ |
29 | data[NFT_REG_VERDICT].verdict = NF_DROP; | ||
30 | break; | 60 | break; |
31 | } | 61 | } |
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; | ||
32 | } | 117 | } |
33 | 118 | ||
34 | static struct nft_expr_type nft_reject_bridge_type; | 119 | static struct nft_expr_type nft_reject_bridge_type; |
@@ -36,8 +121,8 @@ static const struct nft_expr_ops nft_reject_bridge_ops = { | |||
36 | .type = &nft_reject_bridge_type, | 121 | .type = &nft_reject_bridge_type, |
37 | .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), | 122 | .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), |
38 | .eval = nft_reject_bridge_eval, | 123 | .eval = nft_reject_bridge_eval, |
39 | .init = nft_reject_init, | 124 | .init = nft_reject_bridge_init, |
40 | .dump = nft_reject_dump, | 125 | .dump = nft_reject_bridge_dump, |
41 | }; | 126 | }; |
42 | 127 | ||
43 | static struct nft_expr_type nft_reject_bridge_type __read_mostly = { | 128 | static struct nft_expr_type nft_reject_bridge_type __read_mostly = { |
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index e79718a382f2..ed33299c56d1 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/netfilter.h> | 16 | #include <linux/netfilter.h> |
17 | #include <linux/netfilter/nf_tables.h> | 17 | #include <linux/netfilter/nf_tables.h> |
18 | #include <net/netfilter/nf_tables.h> | 18 | #include <net/netfilter/nf_tables.h> |
19 | #include <net/icmp.h> | ||
20 | #include <net/netfilter/ipv4/nf_reject.h> | 19 | #include <net/netfilter/ipv4/nf_reject.h> |
21 | #include <net/netfilter/nft_reject.h> | 20 | #include <net/netfilter/nft_reject.h> |
22 | 21 | ||
diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c index f3448c296446..ec8a456092a7 100644 --- a/net/netfilter/nft_reject.c +++ b/net/netfilter/nft_reject.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/netfilter/nf_tables.h> | 17 | #include <linux/netfilter/nf_tables.h> |
18 | #include <net/netfilter/nf_tables.h> | 18 | #include <net/netfilter/nf_tables.h> |
19 | #include <net/netfilter/nft_reject.h> | 19 | #include <net/netfilter/nft_reject.h> |
20 | #include <linux/icmp.h> | ||
21 | #include <linux/icmpv6.h> | ||
20 | 22 | ||
21 | const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = { | 23 | const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = { |
22 | [NFTA_REJECT_TYPE] = { .type = NLA_U32 }, | 24 | [NFTA_REJECT_TYPE] = { .type = NLA_U32 }, |
@@ -70,5 +72,40 @@ nla_put_failure: | |||
70 | } | 72 | } |
71 | EXPORT_SYMBOL_GPL(nft_reject_dump); | 73 | EXPORT_SYMBOL_GPL(nft_reject_dump); |
72 | 74 | ||
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 | |||
73 | MODULE_LICENSE("GPL"); | 110 | MODULE_LICENSE("GPL"); |
74 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | 111 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); |
diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index b718a52a4654..7b5f9d58680a 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c | |||
@@ -14,17 +14,103 @@ | |||
14 | #include <linux/netfilter/nf_tables.h> | 14 | #include <linux/netfilter/nf_tables.h> |
15 | #include <net/netfilter/nf_tables.h> | 15 | #include <net/netfilter/nf_tables.h> |
16 | #include <net/netfilter/nft_reject.h> | 16 | #include <net/netfilter/nft_reject.h> |
17 | #include <net/netfilter/ipv4/nf_reject.h> | ||
18 | #include <net/netfilter/ipv6/nf_reject.h> | ||
17 | 19 | ||
18 | static void nft_reject_inet_eval(const struct nft_expr *expr, | 20 | static void nft_reject_inet_eval(const struct nft_expr *expr, |
19 | struct nft_data data[NFT_REG_MAX + 1], | 21 | struct nft_data data[NFT_REG_MAX + 1], |
20 | const struct nft_pktinfo *pkt) | 22 | const struct nft_pktinfo *pkt) |
21 | { | 23 | { |
24 | struct nft_reject *priv = nft_expr_priv(expr); | ||
25 | struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); | ||
26 | |||
22 | switch (pkt->ops->pf) { | 27 | switch (pkt->ops->pf) { |
23 | case NFPROTO_IPV4: | 28 | 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; | ||
25 | case NFPROTO_IPV6: | 42 | 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; | ||
27 | } | 90 | } |
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; | ||
28 | } | 114 | } |
29 | 115 | ||
30 | static struct nft_expr_type nft_reject_inet_type; | 116 | static struct nft_expr_type nft_reject_inet_type; |
@@ -32,8 +118,8 @@ static const struct nft_expr_ops nft_reject_inet_ops = { | |||
32 | .type = &nft_reject_inet_type, | 118 | .type = &nft_reject_inet_type, |
33 | .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), | 119 | .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), |
34 | .eval = nft_reject_inet_eval, | 120 | .eval = nft_reject_inet_eval, |
35 | .init = nft_reject_init, | 121 | .init = nft_reject_inet_init, |
36 | .dump = nft_reject_dump, | 122 | .dump = nft_reject_inet_dump, |
37 | }; | 123 | }; |
38 | 124 | ||
39 | static struct nft_expr_type nft_reject_inet_type __read_mostly = { | 125 | static struct nft_expr_type nft_reject_inet_type __read_mostly = { |