aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Schillstrom <hans.schillstrom@ericsson.com>2012-05-02 03:49:47 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2012-05-09 06:54:05 -0400
commitcf308a1fae432f315989e2da6878bfaa3daa22b1 (patch)
tree1997e797a147cc51df1b51eaf9744e84aa956838
parent84018f55ab883f03d41ec3c9ac7f0cc80830b20f (diff)
netfilter: add xt_hmark target for hash-based skb marking
The target allows you to create rules in the "raw" and "mangle" tables which set the skbuff mark by means of hash calculation within a given range. The nfmark can influence the routing method (see "Use netfilter MARK value as routing key") and can also be used by other subsystems to change their behaviour. [ Part of this patch has been refactorized and modified by Pablo Neira Ayuso ] Signed-off-by: Hans Schillstrom <hans.schillstrom@ericsson.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/xt_HMARK.h45
-rw-r--r--net/netfilter/Kconfig15
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/xt_HMARK.c362
4 files changed, 423 insertions, 0 deletions
diff --git a/include/linux/netfilter/xt_HMARK.h b/include/linux/netfilter/xt_HMARK.h
new file mode 100644
index 000000000000..abb1650940d2
--- /dev/null
+++ b/include/linux/netfilter/xt_HMARK.h
@@ -0,0 +1,45 @@
1#ifndef XT_HMARK_H_
2#define XT_HMARK_H_
3
4#include <linux/types.h>
5
6enum {
7 XT_HMARK_SADDR_MASK,
8 XT_HMARK_DADDR_MASK,
9 XT_HMARK_SPI,
10 XT_HMARK_SPI_MASK,
11 XT_HMARK_SPORT,
12 XT_HMARK_DPORT,
13 XT_HMARK_SPORT_MASK,
14 XT_HMARK_DPORT_MASK,
15 XT_HMARK_PROTO_MASK,
16 XT_HMARK_RND,
17 XT_HMARK_MODULUS,
18 XT_HMARK_OFFSET,
19 XT_HMARK_CT,
20 XT_HMARK_METHOD_L3,
21 XT_HMARK_METHOD_L3_4,
22};
23#define XT_HMARK_FLAG(flag) (1 << flag)
24
25union hmark_ports {
26 struct {
27 __u16 src;
28 __u16 dst;
29 } p16;
30 __u32 v32;
31};
32
33struct xt_hmark_info {
34 union nf_inet_addr src_mask;
35 union nf_inet_addr dst_mask;
36 union hmark_ports port_mask;
37 union hmark_ports port_set;
38 __u32 flags;
39 __u16 proto_mask;
40 __u32 hashrnd;
41 __u32 hmodulus;
42 __u32 hoffset; /* Mark offset to start from */
43};
44
45#endif /* XT_HMARK_H_ */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 0c6f67e8f2e5..209c1ed43368 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -509,6 +509,21 @@ config NETFILTER_XT_TARGET_HL
509 since you can easily create immortal packets that loop 509 since you can easily create immortal packets that loop
510 forever on the network. 510 forever on the network.
511 511
512config NETFILTER_XT_TARGET_HMARK
513 tristate '"HMARK" target support'
514 depends on (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)
515 depends on NETFILTER_ADVANCED
516 ---help---
517 This option adds the "HMARK" target.
518
519 The target allows you to create rules in the "raw" and "mangle" tables
520 which set the skbuff mark by means of hash calculation within a given
521 range. The nfmark can influence the routing method (see "Use netfilter
522 MARK value as routing key") and can also be used by other subsystems to
523 change their behaviour.
524
525 To compile it as a module, choose M here. If unsure, say N.
526
512config NETFILTER_XT_TARGET_IDLETIMER 527config NETFILTER_XT_TARGET_IDLETIMER
513 tristate "IDLETIMER target support" 528 tristate "IDLETIMER target support"
514 depends on NETFILTER_ADVANCED 529 depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index ca3676586f51..4e7960cc7b97 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
59obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o 59obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
60obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o 60obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
61obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o 61obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
62obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o
62obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o 63obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
63obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o 64obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o
64obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o 65obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c
new file mode 100644
index 000000000000..32fbd735d02b
--- /dev/null
+++ b/net/netfilter/xt_HMARK.c
@@ -0,0 +1,362 @@
1/*
2 * xt_HMARK - Netfilter module to set mark by means of hashing
3 *
4 * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
5 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 */
11
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/icmp.h>
15
16#include <linux/netfilter/x_tables.h>
17#include <linux/netfilter/xt_HMARK.h>
18
19#include <net/ip.h>
20#if IS_ENABLED(CONFIG_NF_CONNTRACK)
21#include <net/netfilter/nf_conntrack.h>
22#endif
23#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
24#include <net/ipv6.h>
25#include <linux/netfilter_ipv6/ip6_tables.h>
26#endif
27
28MODULE_LICENSE("GPL");
29MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
30MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
31MODULE_ALIAS("ipt_HMARK");
32MODULE_ALIAS("ip6t_HMARK");
33
34struct hmark_tuple {
35 u32 src;
36 u32 dst;
37 union hmark_ports uports;
38 uint8_t proto;
39};
40
41static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask)
42{
43 return (addr32[0] & mask[0]) ^
44 (addr32[1] & mask[1]) ^
45 (addr32[2] & mask[2]) ^
46 (addr32[3] & mask[3]);
47}
48
49static inline u32
50hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask)
51{
52 switch (l3num) {
53 case AF_INET:
54 return *addr32 & *mask;
55 case AF_INET6:
56 return hmark_addr6_mask(addr32, mask);
57 }
58 return 0;
59}
60
61static int
62hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
63 const struct xt_hmark_info *info)
64{
65#if IS_ENABLED(CONFIG_NF_CONNTRACK)
66 enum ip_conntrack_info ctinfo;
67 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
68 struct nf_conntrack_tuple *otuple;
69 struct nf_conntrack_tuple *rtuple;
70
71 if (ct == NULL || nf_ct_is_untracked(ct))
72 return -1;
73
74 otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
75 rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
76
77 t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all,
78 info->src_mask.all);
79 t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all,
80 info->dst_mask.all);
81
82 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
83 return 0;
84
85 t->proto = nf_ct_protonum(ct);
86 if (t->proto != IPPROTO_ICMP) {
87 t->uports.p16.src = otuple->src.u.all;
88 t->uports.p16.dst = rtuple->src.u.all;
89 t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
90 info->port_set.v32;
91 if (t->uports.p16.dst < t->uports.p16.src)
92 swap(t->uports.p16.dst, t->uports.p16.src);
93 }
94
95 return 0;
96#else
97 return -1;
98#endif
99}
100
101static inline u32
102hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
103{
104 u32 hash;
105
106 if (t->dst < t->src)
107 swap(t->src, t->dst);
108
109 hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd);
110 hash = hash ^ (t->proto & info->proto_mask);
111
112 return (hash % info->hmodulus) + info->hoffset;
113}
114
115static void
116hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
117 struct hmark_tuple *t, const struct xt_hmark_info *info)
118{
119 int protoff;
120
121 protoff = proto_ports_offset(t->proto);
122 if (protoff < 0)
123 return;
124
125 nhoff += protoff;
126 if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
127 return;
128
129 t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
130 info->port_set.v32;
131
132 if (t->uports.p16.dst < t->uports.p16.src)
133 swap(t->uports.p16.dst, t->uports.p16.src);
134}
135
136#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
137static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
138{
139 struct icmp6hdr *icmp6h, _ih6;
140
141 icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
142 if (icmp6h == NULL)
143 return 0;
144
145 if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
146 *offset += sizeof(struct icmp6hdr);
147 return 1;
148 }
149 return 0;
150}
151
152static int
153hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
154 const struct xt_hmark_info *info)
155{
156 struct ipv6hdr *ip6, _ip6;
157 int flag = IP6T_FH_F_AUTH;
158 unsigned int nhoff = 0;
159 u16 fragoff = 0;
160 int nexthdr;
161
162 ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
163 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
164 if (nexthdr < 0)
165 return 0;
166 /* No need to check for icmp errors on fragments */
167 if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
168 goto noicmp;
169 /* Use inner header in case of ICMP errors */
170 if (get_inner6_hdr(skb, &nhoff)) {
171 ip6 = skb_header_pointer(skb, nhoff, sizeof(_ip6), &_ip6);
172 if (ip6 == NULL)
173 return -1;
174 /* If AH present, use SPI like in ESP. */
175 flag = IP6T_FH_F_AUTH;
176 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
177 if (nexthdr < 0)
178 return -1;
179 }
180noicmp:
181 t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.all);
182 t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.all);
183
184 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
185 return 0;
186
187 t->proto = nexthdr;
188 if (t->proto == IPPROTO_ICMPV6)
189 return 0;
190
191 if (flag & IP6T_FH_F_FRAG)
192 return 0;
193
194 hmark_set_tuple_ports(skb, nhoff, t, info);
195 return 0;
196}
197
198static unsigned int
199hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
200{
201 const struct xt_hmark_info *info = par->targinfo;
202 struct hmark_tuple t;
203
204 memset(&t, 0, sizeof(struct hmark_tuple));
205
206 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
207 if (hmark_ct_set_htuple(skb, &t, info) < 0)
208 return XT_CONTINUE;
209 } else {
210 if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
211 return XT_CONTINUE;
212 }
213
214 skb->mark = hmark_hash(&t, info);
215 return XT_CONTINUE;
216}
217#endif
218
219static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
220{
221 const struct icmphdr *icmph;
222 struct icmphdr _ih;
223
224 /* Not enough header? */
225 icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
226 if (icmph == NULL && icmph->type > NR_ICMP_TYPES)
227 return 0;
228
229 /* Error message? */
230 if (icmph->type != ICMP_DEST_UNREACH &&
231 icmph->type != ICMP_SOURCE_QUENCH &&
232 icmph->type != ICMP_TIME_EXCEEDED &&
233 icmph->type != ICMP_PARAMETERPROB &&
234 icmph->type != ICMP_REDIRECT)
235 return 0;
236
237 *nhoff += iphsz + sizeof(_ih);
238 return 1;
239}
240
241static int
242hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
243 const struct xt_hmark_info *info)
244{
245 struct iphdr *ip, _ip;
246 int nhoff = skb_network_offset(skb);
247
248 ip = (struct iphdr *) (skb->data + nhoff);
249 if (ip->protocol == IPPROTO_ICMP) {
250 /* Use inner header in case of ICMP errors */
251 if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
252 ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
253 if (ip == NULL)
254 return -1;
255 }
256 }
257
258 t->src = (__force u32) ip->saddr;
259 t->dst = (__force u32) ip->daddr;
260
261 t->src &= info->src_mask.ip;
262 t->dst &= info->dst_mask.ip;
263
264 if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
265 return 0;
266
267 t->proto = ip->protocol;
268
269 /* ICMP has no ports, skip */
270 if (t->proto == IPPROTO_ICMP)
271 return 0;
272
273 /* follow-up fragments don't contain ports, skip all fragments */
274 if (ip->frag_off & htons(IP_MF | IP_OFFSET))
275 return 0;
276
277 hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
278
279 return 0;
280}
281
282static unsigned int
283hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
284{
285 const struct xt_hmark_info *info = par->targinfo;
286 struct hmark_tuple t;
287
288 memset(&t, 0, sizeof(struct hmark_tuple));
289
290 if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
291 if (hmark_ct_set_htuple(skb, &t, info) < 0)
292 return XT_CONTINUE;
293 } else {
294 if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
295 return XT_CONTINUE;
296 }
297
298 skb->mark = hmark_hash(&t, info);
299 return XT_CONTINUE;
300}
301
302static int hmark_tg_check(const struct xt_tgchk_param *par)
303{
304 const struct xt_hmark_info *info = par->targinfo;
305
306 if (!info->hmodulus) {
307 pr_info("xt_HMARK: hash modulus can't be zero\n");
308 return -EINVAL;
309 }
310 if (info->proto_mask &&
311 (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))) {
312 pr_info("xt_HMARK: proto mask must be zero with L3 mode\n");
313 return -EINVAL;
314 }
315 if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK) &&
316 (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK) |
317 XT_HMARK_FLAG(XT_HMARK_DPORT_MASK)))) {
318 pr_info("xt_HMARK: spi-mask and port-mask can't be combined\n");
319 return -EINVAL;
320 }
321 if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI) &&
322 (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT) |
323 XT_HMARK_FLAG(XT_HMARK_DPORT)))) {
324 pr_info("xt_HMARK: spi-set and port-set can't be combined\n");
325 return -EINVAL;
326 }
327 return 0;
328}
329
330static struct xt_target hmark_tg_reg[] __read_mostly = {
331 {
332 .name = "HMARK",
333 .family = NFPROTO_IPV4,
334 .target = hmark_tg_v4,
335 .targetsize = sizeof(struct xt_hmark_info),
336 .checkentry = hmark_tg_check,
337 .me = THIS_MODULE,
338 },
339#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
340 {
341 .name = "HMARK",
342 .family = NFPROTO_IPV6,
343 .target = hmark_tg_v6,
344 .targetsize = sizeof(struct xt_hmark_info),
345 .checkentry = hmark_tg_check,
346 .me = THIS_MODULE,
347 },
348#endif
349};
350
351static int __init hmark_tg_init(void)
352{
353 return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
354}
355
356static void __exit hmark_tg_exit(void)
357{
358 xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
359}
360
361module_init(hmark_tg_init);
362module_exit(hmark_tg_exit);