diff options
author | David S. Miller <davem@davemloft.net> | 2012-05-09 18:07:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-05-09 18:07:44 -0400 |
commit | a49d1a905ed96c7a790d9535e2bc62d3f2f55a05 (patch) | |
tree | 709bdaab23aa7ef98aad0cdfd4d8ecd8324650cc | |
parent | 2e7d21c54adbab6d10481eddc685328f89bb6389 (diff) | |
parent | 0197dee7d3182bb6b6a21955860dfa14fa022d84 (diff) |
Merge git://1984.lsi.us.es/net-next
-rw-r--r-- | include/linux/netfilter/xt_HMARK.h | 45 | ||||
-rw-r--r-- | include/linux/netfilter/xt_hashlimit.h | 10 | ||||
-rw-r--r-- | include/linux/netfilter_ipv6/ip6_tables.h | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 36 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_ah.c | 4 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_frag.c | 4 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_hbh.c | 4 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_rt.c | 4 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 15 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_HMARK.c | 362 | ||||
-rw-r--r-- | net/netfilter/xt_TPROXY.c | 4 | ||||
-rw-r--r-- | net/netfilter/xt_hashlimit.c | 129 | ||||
-rw-r--r-- | net/netfilter/xt_limit.c | 5 | ||||
-rw-r--r-- | net/netfilter/xt_socket.c | 4 |
15 files changed, 588 insertions, 46 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 | |||
6 | enum { | ||
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 | |||
25 | union hmark_ports { | ||
26 | struct { | ||
27 | __u16 src; | ||
28 | __u16 dst; | ||
29 | } p16; | ||
30 | __u32 v32; | ||
31 | }; | ||
32 | |||
33 | struct 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/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h index b1925b5925e9..05fe7993dd76 100644 --- a/include/linux/netfilter/xt_hashlimit.h +++ b/include/linux/netfilter/xt_hashlimit.h | |||
@@ -6,7 +6,11 @@ | |||
6 | /* timings are in milliseconds. */ | 6 | /* timings are in milliseconds. */ |
7 | #define XT_HASHLIMIT_SCALE 10000 | 7 | #define XT_HASHLIMIT_SCALE 10000 |
8 | /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 | 8 | /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 |
9 | seconds, or one every 59 hours. */ | 9 | * seconds, or one packet every 59 hours. |
10 | */ | ||
11 | |||
12 | /* packet length accounting is done in 16-byte steps */ | ||
13 | #define XT_HASHLIMIT_BYTE_SHIFT 4 | ||
10 | 14 | ||
11 | /* details of this structure hidden by the implementation */ | 15 | /* details of this structure hidden by the implementation */ |
12 | struct xt_hashlimit_htable; | 16 | struct xt_hashlimit_htable; |
@@ -17,6 +21,10 @@ enum { | |||
17 | XT_HASHLIMIT_HASH_SIP = 1 << 2, | 21 | XT_HASHLIMIT_HASH_SIP = 1 << 2, |
18 | XT_HASHLIMIT_HASH_SPT = 1 << 3, | 22 | XT_HASHLIMIT_HASH_SPT = 1 << 3, |
19 | XT_HASHLIMIT_INVERT = 1 << 4, | 23 | XT_HASHLIMIT_INVERT = 1 << 4, |
24 | XT_HASHLIMIT_BYTES = 1 << 5, | ||
25 | #ifdef __KERNEL__ | ||
26 | XT_HASHLIMIT_MAX = 1 << 6, | ||
27 | #endif | ||
20 | }; | 28 | }; |
21 | 29 | ||
22 | struct hashlimit_cfg { | 30 | struct hashlimit_cfg { |
diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 1bc898b14a80..08c2cbbaa32b 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h | |||
@@ -298,9 +298,14 @@ ip6t_ext_hdr(u8 nexthdr) | |||
298 | (nexthdr == IPPROTO_DSTOPTS); | 298 | (nexthdr == IPPROTO_DSTOPTS); |
299 | } | 299 | } |
300 | 300 | ||
301 | enum { | ||
302 | IP6T_FH_F_FRAG = (1 << 0), | ||
303 | IP6T_FH_F_AUTH = (1 << 1), | ||
304 | }; | ||
305 | |||
301 | /* find specified header and get offset to it */ | 306 | /* find specified header and get offset to it */ |
302 | extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | 307 | extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, |
303 | int target, unsigned short *fragoff); | 308 | int target, unsigned short *fragoff, int *fragflg); |
304 | 309 | ||
305 | #ifdef CONFIG_COMPAT | 310 | #ifdef CONFIG_COMPAT |
306 | #include <net/compat.h> | 311 | #include <net/compat.h> |
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d4e350f72bbb..308bdd651230 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -133,7 +133,7 @@ ip6_packet_match(const struct sk_buff *skb, | |||
133 | int protohdr; | 133 | int protohdr; |
134 | unsigned short _frag_off; | 134 | unsigned short _frag_off; |
135 | 135 | ||
136 | protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off); | 136 | protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off, NULL); |
137 | if (protohdr < 0) { | 137 | if (protohdr < 0) { |
138 | if (_frag_off == 0) | 138 | if (_frag_off == 0) |
139 | *hotdrop = true; | 139 | *hotdrop = true; |
@@ -362,6 +362,7 @@ ip6t_do_table(struct sk_buff *skb, | |||
362 | const struct xt_entry_match *ematch; | 362 | const struct xt_entry_match *ematch; |
363 | 363 | ||
364 | IP_NF_ASSERT(e); | 364 | IP_NF_ASSERT(e); |
365 | acpar.thoff = 0; | ||
365 | if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, | 366 | if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, |
366 | &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { | 367 | &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { |
367 | no_match: | 368 | no_match: |
@@ -2278,6 +2279,10 @@ static void __exit ip6_tables_fini(void) | |||
2278 | * if target < 0. "last header" is transport protocol header, ESP, or | 2279 | * if target < 0. "last header" is transport protocol header, ESP, or |
2279 | * "No next header". | 2280 | * "No next header". |
2280 | * | 2281 | * |
2282 | * Note that *offset is used as input/output parameter. an if it is not zero, | ||
2283 | * then it must be a valid offset to an inner IPv6 header. This can be used | ||
2284 | * to explore inner IPv6 header, eg. ICMPv6 error messages. | ||
2285 | * | ||
2281 | * If target header is found, its offset is set in *offset and return protocol | 2286 | * If target header is found, its offset is set in *offset and return protocol |
2282 | * number. Otherwise, return -1. | 2287 | * number. Otherwise, return -1. |
2283 | * | 2288 | * |
@@ -2289,17 +2294,33 @@ static void __exit ip6_tables_fini(void) | |||
2289 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff | 2294 | * *offset is meaningless and fragment offset is stored in *fragoff if fragoff |
2290 | * isn't NULL. | 2295 | * isn't NULL. |
2291 | * | 2296 | * |
2297 | * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG | ||
2298 | * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and | ||
2299 | * target < 0, then this function will stop at the AH header. | ||
2292 | */ | 2300 | */ |
2293 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | 2301 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, |
2294 | int target, unsigned short *fragoff) | 2302 | int target, unsigned short *fragoff, int *flags) |
2295 | { | 2303 | { |
2296 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | 2304 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); |
2297 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | 2305 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; |
2298 | unsigned int len = skb->len - start; | 2306 | unsigned int len; |
2299 | 2307 | ||
2300 | if (fragoff) | 2308 | if (fragoff) |
2301 | *fragoff = 0; | 2309 | *fragoff = 0; |
2302 | 2310 | ||
2311 | if (*offset) { | ||
2312 | struct ipv6hdr _ip6, *ip6; | ||
2313 | |||
2314 | ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); | ||
2315 | if (!ip6 || (ip6->version != 6)) { | ||
2316 | printk(KERN_ERR "IPv6 header not found\n"); | ||
2317 | return -EBADMSG; | ||
2318 | } | ||
2319 | start = *offset + sizeof(struct ipv6hdr); | ||
2320 | nexthdr = ip6->nexthdr; | ||
2321 | } | ||
2322 | len = skb->len - start; | ||
2323 | |||
2303 | while (nexthdr != target) { | 2324 | while (nexthdr != target) { |
2304 | struct ipv6_opt_hdr _hdr, *hp; | 2325 | struct ipv6_opt_hdr _hdr, *hp; |
2305 | unsigned int hdrlen; | 2326 | unsigned int hdrlen; |
@@ -2316,6 +2337,9 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
2316 | if (nexthdr == NEXTHDR_FRAGMENT) { | 2337 | if (nexthdr == NEXTHDR_FRAGMENT) { |
2317 | unsigned short _frag_off; | 2338 | unsigned short _frag_off; |
2318 | __be16 *fp; | 2339 | __be16 *fp; |
2340 | |||
2341 | if (flags) /* Indicate that this is a fragment */ | ||
2342 | *flags |= IP6T_FH_F_FRAG; | ||
2319 | fp = skb_header_pointer(skb, | 2343 | fp = skb_header_pointer(skb, |
2320 | start+offsetof(struct frag_hdr, | 2344 | start+offsetof(struct frag_hdr, |
2321 | frag_off), | 2345 | frag_off), |
@@ -2336,9 +2360,11 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | |||
2336 | return -ENOENT; | 2360 | return -ENOENT; |
2337 | } | 2361 | } |
2338 | hdrlen = 8; | 2362 | hdrlen = 8; |
2339 | } else if (nexthdr == NEXTHDR_AUTH) | 2363 | } else if (nexthdr == NEXTHDR_AUTH) { |
2364 | if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) | ||
2365 | break; | ||
2340 | hdrlen = (hp->hdrlen + 2) << 2; | 2366 | hdrlen = (hp->hdrlen + 2) << 2; |
2341 | else | 2367 | } else |
2342 | hdrlen = ipv6_optlen(hp); | 2368 | hdrlen = ipv6_optlen(hp); |
2343 | 2369 | ||
2344 | nexthdr = hp->nexthdr; | 2370 | nexthdr = hp->nexthdr; |
diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index 89cccc5a9c92..04099ab7d2e3 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c | |||
@@ -41,11 +41,11 @@ static bool ah_mt6(const struct sk_buff *skb, struct xt_action_param *par) | |||
41 | struct ip_auth_hdr _ah; | 41 | struct ip_auth_hdr _ah; |
42 | const struct ip_auth_hdr *ah; | 42 | const struct ip_auth_hdr *ah; |
43 | const struct ip6t_ah *ahinfo = par->matchinfo; | 43 | const struct ip6t_ah *ahinfo = par->matchinfo; |
44 | unsigned int ptr; | 44 | unsigned int ptr = 0; |
45 | unsigned int hdrlen = 0; | 45 | unsigned int hdrlen = 0; |
46 | int err; | 46 | int err; |
47 | 47 | ||
48 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL); | 48 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL, NULL); |
49 | if (err < 0) { | 49 | if (err < 0) { |
50 | if (err != -ENOENT) | 50 | if (err != -ENOENT) |
51 | par->hotdrop = true; | 51 | par->hotdrop = true; |
diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index eda898fda6ca..3b5735e56bfe 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c | |||
@@ -40,10 +40,10 @@ frag_mt6(const struct sk_buff *skb, struct xt_action_param *par) | |||
40 | struct frag_hdr _frag; | 40 | struct frag_hdr _frag; |
41 | const struct frag_hdr *fh; | 41 | const struct frag_hdr *fh; |
42 | const struct ip6t_frag *fraginfo = par->matchinfo; | 42 | const struct ip6t_frag *fraginfo = par->matchinfo; |
43 | unsigned int ptr; | 43 | unsigned int ptr = 0; |
44 | int err; | 44 | int err; |
45 | 45 | ||
46 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL); | 46 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL, NULL); |
47 | if (err < 0) { | 47 | if (err < 0) { |
48 | if (err != -ENOENT) | 48 | if (err != -ENOENT) |
49 | par->hotdrop = true; | 49 | par->hotdrop = true; |
diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index 59df051eaef6..01df142bb027 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c | |||
@@ -50,7 +50,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) | |||
50 | const struct ipv6_opt_hdr *oh; | 50 | const struct ipv6_opt_hdr *oh; |
51 | const struct ip6t_opts *optinfo = par->matchinfo; | 51 | const struct ip6t_opts *optinfo = par->matchinfo; |
52 | unsigned int temp; | 52 | unsigned int temp; |
53 | unsigned int ptr; | 53 | unsigned int ptr = 0; |
54 | unsigned int hdrlen = 0; | 54 | unsigned int hdrlen = 0; |
55 | bool ret = false; | 55 | bool ret = false; |
56 | u8 _opttype; | 56 | u8 _opttype; |
@@ -62,7 +62,7 @@ hbh_mt6(const struct sk_buff *skb, struct xt_action_param *par) | |||
62 | 62 | ||
63 | err = ipv6_find_hdr(skb, &ptr, | 63 | err = ipv6_find_hdr(skb, &ptr, |
64 | (par->match == &hbh_mt6_reg[0]) ? | 64 | (par->match == &hbh_mt6_reg[0]) ? |
65 | NEXTHDR_HOP : NEXTHDR_DEST, NULL); | 65 | NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL); |
66 | if (err < 0) { | 66 | if (err < 0) { |
67 | if (err != -ENOENT) | 67 | if (err != -ENOENT) |
68 | par->hotdrop = true; | 68 | par->hotdrop = true; |
diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index d8488c50a8e0..2c99b94eeca3 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c | |||
@@ -42,14 +42,14 @@ static bool rt_mt6(const struct sk_buff *skb, struct xt_action_param *par) | |||
42 | const struct ipv6_rt_hdr *rh; | 42 | const struct ipv6_rt_hdr *rh; |
43 | const struct ip6t_rt *rtinfo = par->matchinfo; | 43 | const struct ip6t_rt *rtinfo = par->matchinfo; |
44 | unsigned int temp; | 44 | unsigned int temp; |
45 | unsigned int ptr; | 45 | unsigned int ptr = 0; |
46 | unsigned int hdrlen = 0; | 46 | unsigned int hdrlen = 0; |
47 | bool ret = false; | 47 | bool ret = false; |
48 | struct in6_addr _addr; | 48 | struct in6_addr _addr; |
49 | const struct in6_addr *ap; | 49 | const struct in6_addr *ap; |
50 | int err; | 50 | int err; |
51 | 51 | ||
52 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL); | 52 | err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL, NULL); |
53 | if (err < 0) { | 53 | if (err < 0) { |
54 | if (err != -ENOENT) | 54 | if (err != -ENOENT) |
55 | par->hotdrop = true; | 55 | par->hotdrop = true; |
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 | ||
512 | config 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 | |||
512 | config NETFILTER_XT_TARGET_IDLETIMER | 527 | config 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 | |||
59 | obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o | 59 | obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o |
60 | obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o | 60 | obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o |
61 | obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o | 61 | obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o |
62 | obj-$(CONFIG_NETFILTER_XT_TARGET_HMARK) += xt_HMARK.o | ||
62 | obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o | 63 | obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o |
63 | obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o | 64 | obj-$(CONFIG_NETFILTER_XT_TARGET_LOG) += xt_LOG.o |
64 | obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o | 65 | obj-$(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 | |||
28 | MODULE_LICENSE("GPL"); | ||
29 | MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>"); | ||
30 | MODULE_DESCRIPTION("Xtables: packet marking using hash calculation"); | ||
31 | MODULE_ALIAS("ipt_HMARK"); | ||
32 | MODULE_ALIAS("ip6t_HMARK"); | ||
33 | |||
34 | struct hmark_tuple { | ||
35 | u32 src; | ||
36 | u32 dst; | ||
37 | union hmark_ports uports; | ||
38 | uint8_t proto; | ||
39 | }; | ||
40 | |||
41 | static 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 | |||
49 | static inline u32 | ||
50 | hmark_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 | |||
61 | static int | ||
62 | hmark_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 | |||
101 | static inline u32 | ||
102 | hmark_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 | |||
115 | static void | ||
116 | hmark_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) | ||
137 | static 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 | |||
152 | static int | ||
153 | hmark_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 | } | ||
180 | noicmp: | ||
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 | |||
198 | static unsigned int | ||
199 | hmark_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 | |||
219 | static 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 | |||
241 | static int | ||
242 | hmark_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 | |||
282 | static unsigned int | ||
283 | hmark_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 | |||
302 | static 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 | |||
330 | static 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 | |||
351 | static int __init hmark_tg_init(void) | ||
352 | { | ||
353 | return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); | ||
354 | } | ||
355 | |||
356 | static void __exit hmark_tg_exit(void) | ||
357 | { | ||
358 | xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg)); | ||
359 | } | ||
360 | |||
361 | module_init(hmark_tg_init); | ||
362 | module_exit(hmark_tg_exit); | ||
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 35a959a096e0..146033a86de8 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c | |||
@@ -282,10 +282,10 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par) | |||
282 | struct sock *sk; | 282 | struct sock *sk; |
283 | const struct in6_addr *laddr; | 283 | const struct in6_addr *laddr; |
284 | __be16 lport; | 284 | __be16 lport; |
285 | int thoff; | 285 | int thoff = 0; |
286 | int tproto; | 286 | int tproto; |
287 | 287 | ||
288 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); | 288 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); |
289 | if (tproto < 0) { | 289 | if (tproto < 0) { |
290 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); | 290 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); |
291 | return NF_DROP; | 291 | return NF_DROP; |
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index d95f9c963cde..d0424f9621f2 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c | |||
@@ -388,9 +388,20 @@ static void htable_put(struct xt_hashlimit_htable *hinfo) | |||
388 | 388 | ||
389 | #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) | 389 | #define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ) |
390 | 390 | ||
391 | /* in byte mode, the lowest possible rate is one packet/second. | ||
392 | * credit_cap is used as a counter that tells us how many times we can | ||
393 | * refill the "credits available" counter when it becomes empty. | ||
394 | */ | ||
395 | #define MAX_CPJ_BYTES (0xFFFFFFFF / HZ) | ||
396 | #define CREDITS_PER_JIFFY_BYTES POW2_BELOW32(MAX_CPJ_BYTES) | ||
397 | |||
398 | static u32 xt_hashlimit_len_to_chunks(u32 len) | ||
399 | { | ||
400 | return (len >> XT_HASHLIMIT_BYTE_SHIFT) + 1; | ||
401 | } | ||
402 | |||
391 | /* Precision saver. */ | 403 | /* Precision saver. */ |
392 | static inline u_int32_t | 404 | static u32 user2credits(u32 user) |
393 | user2credits(u_int32_t user) | ||
394 | { | 405 | { |
395 | /* If multiplying would overflow... */ | 406 | /* If multiplying would overflow... */ |
396 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) | 407 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) |
@@ -400,12 +411,53 @@ user2credits(u_int32_t user) | |||
400 | return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE; | 411 | return (user * HZ * CREDITS_PER_JIFFY) / XT_HASHLIMIT_SCALE; |
401 | } | 412 | } |
402 | 413 | ||
403 | static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now) | 414 | static u32 user2credits_byte(u32 user) |
404 | { | 415 | { |
405 | dh->rateinfo.credit += (now - dh->rateinfo.prev) * CREDITS_PER_JIFFY; | 416 | u64 us = user; |
406 | if (dh->rateinfo.credit > dh->rateinfo.credit_cap) | 417 | us *= HZ * CREDITS_PER_JIFFY_BYTES; |
407 | dh->rateinfo.credit = dh->rateinfo.credit_cap; | 418 | return (u32) (us >> 32); |
419 | } | ||
420 | |||
421 | static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, u32 mode) | ||
422 | { | ||
423 | unsigned long delta = now - dh->rateinfo.prev; | ||
424 | u32 cap; | ||
425 | |||
426 | if (delta == 0) | ||
427 | return; | ||
428 | |||
408 | dh->rateinfo.prev = now; | 429 | dh->rateinfo.prev = now; |
430 | |||
431 | if (mode & XT_HASHLIMIT_BYTES) { | ||
432 | u32 tmp = dh->rateinfo.credit; | ||
433 | dh->rateinfo.credit += CREDITS_PER_JIFFY_BYTES * delta; | ||
434 | cap = CREDITS_PER_JIFFY_BYTES * HZ; | ||
435 | if (tmp >= dh->rateinfo.credit) {/* overflow */ | ||
436 | dh->rateinfo.credit = cap; | ||
437 | return; | ||
438 | } | ||
439 | } else { | ||
440 | dh->rateinfo.credit += delta * CREDITS_PER_JIFFY; | ||
441 | cap = dh->rateinfo.credit_cap; | ||
442 | } | ||
443 | if (dh->rateinfo.credit > cap) | ||
444 | dh->rateinfo.credit = cap; | ||
445 | } | ||
446 | |||
447 | static void rateinfo_init(struct dsthash_ent *dh, | ||
448 | struct xt_hashlimit_htable *hinfo) | ||
449 | { | ||
450 | dh->rateinfo.prev = jiffies; | ||
451 | if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) { | ||
452 | dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ; | ||
453 | dh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg); | ||
454 | dh->rateinfo.credit_cap = hinfo->cfg.burst; | ||
455 | } else { | ||
456 | dh->rateinfo.credit = user2credits(hinfo->cfg.avg * | ||
457 | hinfo->cfg.burst); | ||
458 | dh->rateinfo.cost = user2credits(hinfo->cfg.avg); | ||
459 | dh->rateinfo.credit_cap = dh->rateinfo.credit; | ||
460 | } | ||
409 | } | 461 | } |
410 | 462 | ||
411 | static inline __be32 maskl(__be32 a, unsigned int l) | 463 | static inline __be32 maskl(__be32 a, unsigned int l) |
@@ -511,6 +563,21 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, | |||
511 | return 0; | 563 | return 0; |
512 | } | 564 | } |
513 | 565 | ||
566 | static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh) | ||
567 | { | ||
568 | u64 tmp = xt_hashlimit_len_to_chunks(len); | ||
569 | tmp = tmp * dh->rateinfo.cost; | ||
570 | |||
571 | if (unlikely(tmp > CREDITS_PER_JIFFY_BYTES * HZ)) | ||
572 | tmp = CREDITS_PER_JIFFY_BYTES * HZ; | ||
573 | |||
574 | if (dh->rateinfo.credit < tmp && dh->rateinfo.credit_cap) { | ||
575 | dh->rateinfo.credit_cap--; | ||
576 | dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ; | ||
577 | } | ||
578 | return (u32) tmp; | ||
579 | } | ||
580 | |||
514 | static bool | 581 | static bool |
515 | hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) | 582 | hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) |
516 | { | 583 | { |
@@ -519,6 +586,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
519 | unsigned long now = jiffies; | 586 | unsigned long now = jiffies; |
520 | struct dsthash_ent *dh; | 587 | struct dsthash_ent *dh; |
521 | struct dsthash_dst dst; | 588 | struct dsthash_dst dst; |
589 | u32 cost; | ||
522 | 590 | ||
523 | if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) | 591 | if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) |
524 | goto hotdrop; | 592 | goto hotdrop; |
@@ -532,21 +600,21 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
532 | goto hotdrop; | 600 | goto hotdrop; |
533 | } | 601 | } |
534 | dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire); | 602 | dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire); |
535 | dh->rateinfo.prev = jiffies; | 603 | rateinfo_init(dh, hinfo); |
536 | dh->rateinfo.credit = user2credits(hinfo->cfg.avg * | ||
537 | hinfo->cfg.burst); | ||
538 | dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * | ||
539 | hinfo->cfg.burst); | ||
540 | dh->rateinfo.cost = user2credits(hinfo->cfg.avg); | ||
541 | } else { | 604 | } else { |
542 | /* update expiration timeout */ | 605 | /* update expiration timeout */ |
543 | dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire); | 606 | dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire); |
544 | rateinfo_recalc(dh, now); | 607 | rateinfo_recalc(dh, now, hinfo->cfg.mode); |
545 | } | 608 | } |
546 | 609 | ||
547 | if (dh->rateinfo.credit >= dh->rateinfo.cost) { | 610 | if (info->cfg.mode & XT_HASHLIMIT_BYTES) |
611 | cost = hashlimit_byte_cost(skb->len, dh); | ||
612 | else | ||
613 | cost = dh->rateinfo.cost; | ||
614 | |||
615 | if (dh->rateinfo.credit >= cost) { | ||
548 | /* below the limit */ | 616 | /* below the limit */ |
549 | dh->rateinfo.credit -= dh->rateinfo.cost; | 617 | dh->rateinfo.credit -= cost; |
550 | spin_unlock(&dh->lock); | 618 | spin_unlock(&dh->lock); |
551 | rcu_read_unlock_bh(); | 619 | rcu_read_unlock_bh(); |
552 | return !(info->cfg.mode & XT_HASHLIMIT_INVERT); | 620 | return !(info->cfg.mode & XT_HASHLIMIT_INVERT); |
@@ -568,14 +636,6 @@ static int hashlimit_mt_check(const struct xt_mtchk_param *par) | |||
568 | struct xt_hashlimit_mtinfo1 *info = par->matchinfo; | 636 | struct xt_hashlimit_mtinfo1 *info = par->matchinfo; |
569 | int ret; | 637 | int ret; |
570 | 638 | ||
571 | /* Check for overflow. */ | ||
572 | if (info->cfg.burst == 0 || | ||
573 | user2credits(info->cfg.avg * info->cfg.burst) < | ||
574 | user2credits(info->cfg.avg)) { | ||
575 | pr_info("overflow, try lower: %u/%u\n", | ||
576 | info->cfg.avg, info->cfg.burst); | ||
577 | return -ERANGE; | ||
578 | } | ||
579 | if (info->cfg.gc_interval == 0 || info->cfg.expire == 0) | 639 | if (info->cfg.gc_interval == 0 || info->cfg.expire == 0) |
580 | return -EINVAL; | 640 | return -EINVAL; |
581 | if (info->name[sizeof(info->name)-1] != '\0') | 641 | if (info->name[sizeof(info->name)-1] != '\0') |
@@ -588,6 +648,26 @@ static int hashlimit_mt_check(const struct xt_mtchk_param *par) | |||
588 | return -EINVAL; | 648 | return -EINVAL; |
589 | } | 649 | } |
590 | 650 | ||
651 | if (info->cfg.mode >= XT_HASHLIMIT_MAX) { | ||
652 | pr_info("Unknown mode mask %X, kernel too old?\n", | ||
653 | info->cfg.mode); | ||
654 | return -EINVAL; | ||
655 | } | ||
656 | |||
657 | /* Check for overflow. */ | ||
658 | if (info->cfg.mode & XT_HASHLIMIT_BYTES) { | ||
659 | if (user2credits_byte(info->cfg.avg) == 0) { | ||
660 | pr_info("overflow, rate too high: %u\n", info->cfg.avg); | ||
661 | return -EINVAL; | ||
662 | } | ||
663 | } else if (info->cfg.burst == 0 || | ||
664 | user2credits(info->cfg.avg * info->cfg.burst) < | ||
665 | user2credits(info->cfg.avg)) { | ||
666 | pr_info("overflow, try lower: %u/%u\n", | ||
667 | info->cfg.avg, info->cfg.burst); | ||
668 | return -ERANGE; | ||
669 | } | ||
670 | |||
591 | mutex_lock(&hashlimit_mutex); | 671 | mutex_lock(&hashlimit_mutex); |
592 | info->hinfo = htable_find_get(net, info->name, par->family); | 672 | info->hinfo = htable_find_get(net, info->name, par->family); |
593 | if (info->hinfo == NULL) { | 673 | if (info->hinfo == NULL) { |
@@ -680,10 +760,11 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, | |||
680 | struct seq_file *s) | 760 | struct seq_file *s) |
681 | { | 761 | { |
682 | int res; | 762 | int res; |
763 | const struct xt_hashlimit_htable *ht = s->private; | ||
683 | 764 | ||
684 | spin_lock(&ent->lock); | 765 | spin_lock(&ent->lock); |
685 | /* recalculate to show accurate numbers */ | 766 | /* recalculate to show accurate numbers */ |
686 | rateinfo_recalc(ent, jiffies); | 767 | rateinfo_recalc(ent, jiffies, ht->cfg.mode); |
687 | 768 | ||
688 | switch (family) { | 769 | switch (family) { |
689 | case NFPROTO_IPV4: | 770 | case NFPROTO_IPV4: |
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index 32b7a579a032..5c22ce8ab309 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c | |||
@@ -88,8 +88,7 @@ limit_mt(const struct sk_buff *skb, struct xt_action_param *par) | |||
88 | } | 88 | } |
89 | 89 | ||
90 | /* Precision saver. */ | 90 | /* Precision saver. */ |
91 | static u_int32_t | 91 | static u32 user2credits(u32 user) |
92 | user2credits(u_int32_t user) | ||
93 | { | 92 | { |
94 | /* If multiplying would overflow... */ | 93 | /* If multiplying would overflow... */ |
95 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) | 94 | if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY)) |
@@ -123,7 +122,7 @@ static int limit_mt_check(const struct xt_mtchk_param *par) | |||
123 | 128. */ | 122 | 128. */ |
124 | priv->prev = jiffies; | 123 | priv->prev = jiffies; |
125 | priv->credit = user2credits(r->avg * r->burst); /* Credits full. */ | 124 | priv->credit = user2credits(r->avg * r->burst); /* Credits full. */ |
126 | r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ | 125 | r->credit_cap = priv->credit; /* Credits full. */ |
127 | r->cost = user2credits(r->avg); | 126 | r->cost = user2credits(r->avg); |
128 | } | 127 | } |
129 | return 0; | 128 | return 0; |
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 72bb07f57f97..9ea482d08cf7 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c | |||
@@ -263,10 +263,10 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) | |||
263 | struct sock *sk; | 263 | struct sock *sk; |
264 | struct in6_addr *daddr, *saddr; | 264 | struct in6_addr *daddr, *saddr; |
265 | __be16 dport, sport; | 265 | __be16 dport, sport; |
266 | int thoff, tproto; | 266 | int thoff = 0, tproto; |
267 | const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; | 267 | const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; |
268 | 268 | ||
269 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); | 269 | tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); |
270 | if (tproto < 0) { | 270 | if (tproto < 0) { |
271 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); | 271 | pr_debug("unable to find transport header in IPv6 packet, dropping\n"); |
272 | return NF_DROP; | 272 | return NF_DROP; |