diff options
Diffstat (limited to 'net/ipv6/netfilter/ip6_tables.c')
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 116 |
1 files changed, 106 insertions, 10 deletions
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 125a90d6a79..14cb310064f 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -78,6 +78,19 @@ EXPORT_SYMBOL_GPL(ip6t_alloc_initial_table); | |||
78 | 78 | ||
79 | Hence the start of any table is given by get_table() below. */ | 79 | Hence the start of any table is given by get_table() below. */ |
80 | 80 | ||
81 | /* Check for an extension */ | ||
82 | int | ||
83 | ip6t_ext_hdr(u8 nexthdr) | ||
84 | { | ||
85 | return (nexthdr == IPPROTO_HOPOPTS) || | ||
86 | (nexthdr == IPPROTO_ROUTING) || | ||
87 | (nexthdr == IPPROTO_FRAGMENT) || | ||
88 | (nexthdr == IPPROTO_ESP) || | ||
89 | (nexthdr == IPPROTO_AH) || | ||
90 | (nexthdr == IPPROTO_NONE) || | ||
91 | (nexthdr == IPPROTO_DSTOPTS); | ||
92 | } | ||
93 | |||
81 | /* Returns whether matches rule or not. */ | 94 | /* Returns whether matches rule or not. */ |
82 | /* Performance critical - called for every packet */ | 95 | /* Performance critical - called for every packet */ |
83 | static inline bool | 96 | static inline bool |
@@ -133,7 +146,7 @@ ip6_packet_match(const struct sk_buff *skb, | |||
133 | int protohdr; | 146 | int protohdr; |
134 | unsigned short _frag_off; | 147 | unsigned short _frag_off; |
135 | 148 | ||
136 | protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off, NULL); | 149 | protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off); |
137 | if (protohdr < 0) { | 150 | if (protohdr < 0) { |
138 | if (_frag_off == 0) | 151 | if (_frag_off == 0) |
139 | *hotdrop = true; | 152 | *hotdrop = true; |
@@ -181,7 +194,8 @@ ip6_checkentry(const struct ip6t_ip6 *ipv6) | |||
181 | static unsigned int | 194 | static unsigned int |
182 | ip6t_error(struct sk_buff *skb, const struct xt_action_param *par) | 195 | ip6t_error(struct sk_buff *skb, const struct xt_action_param *par) |
183 | { | 196 | { |
184 | net_info_ratelimited("error: `%s'\n", (const char *)par->targinfo); | 197 | if (net_ratelimit()) |
198 | pr_info("error: `%s'\n", (const char *)par->targinfo); | ||
185 | 199 | ||
186 | return NF_DROP; | 200 | return NF_DROP; |
187 | } | 201 | } |
@@ -207,7 +221,8 @@ ip6t_get_target_c(const struct ip6t_entry *e) | |||
207 | return ip6t_get_target((struct ip6t_entry *)e); | 221 | return ip6t_get_target((struct ip6t_entry *)e); |
208 | } | 222 | } |
209 | 223 | ||
210 | #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) | 224 | #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ |
225 | defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE) | ||
211 | /* This cries for unification! */ | 226 | /* This cries for unification! */ |
212 | static const char *const hooknames[] = { | 227 | static const char *const hooknames[] = { |
213 | [NF_INET_PRE_ROUTING] = "PREROUTING", | 228 | [NF_INET_PRE_ROUTING] = "PREROUTING", |
@@ -360,7 +375,6 @@ ip6t_do_table(struct sk_buff *skb, | |||
360 | const struct xt_entry_match *ematch; | 375 | const struct xt_entry_match *ematch; |
361 | 376 | ||
362 | IP_NF_ASSERT(e); | 377 | IP_NF_ASSERT(e); |
363 | acpar.thoff = 0; | ||
364 | if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, | 378 | if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, |
365 | &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { | 379 | &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { |
366 | no_match: | 380 | no_match: |
@@ -380,7 +394,8 @@ ip6t_do_table(struct sk_buff *skb, | |||
380 | t = ip6t_get_target_c(e); | 394 | t = ip6t_get_target_c(e); |
381 | IP_NF_ASSERT(t->u.kernel.target); | 395 | IP_NF_ASSERT(t->u.kernel.target); |
382 | 396 | ||
383 | #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) | 397 | #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \ |
398 | defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE) | ||
384 | /* The packet is traced: log it */ | 399 | /* The packet is traced: log it */ |
385 | if (unlikely(skb->nf_trace)) | 400 | if (unlikely(skb->nf_trace)) |
386 | trace_packet(skb, hook, in, out, | 401 | trace_packet(skb, hook, in, out, |
@@ -394,7 +409,7 @@ ip6t_do_table(struct sk_buff *skb, | |||
394 | if (v < 0) { | 409 | if (v < 0) { |
395 | /* Pop from stack? */ | 410 | /* Pop from stack? */ |
396 | if (v != XT_RETURN) { | 411 | if (v != XT_RETURN) { |
397 | verdict = (unsigned int)(-v) - 1; | 412 | verdict = (unsigned)(-v) - 1; |
398 | break; | 413 | break; |
399 | } | 414 | } |
400 | if (*stackptr <= origptr) | 415 | if (*stackptr <= origptr) |
@@ -1854,7 +1869,7 @@ compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, | |||
1854 | { | 1869 | { |
1855 | int ret; | 1870 | int ret; |
1856 | 1871 | ||
1857 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) | 1872 | if (!capable(CAP_NET_ADMIN)) |
1858 | return -EPERM; | 1873 | return -EPERM; |
1859 | 1874 | ||
1860 | switch (cmd) { | 1875 | switch (cmd) { |
@@ -1969,7 +1984,7 @@ compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
1969 | { | 1984 | { |
1970 | int ret; | 1985 | int ret; |
1971 | 1986 | ||
1972 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) | 1987 | if (!capable(CAP_NET_ADMIN)) |
1973 | return -EPERM; | 1988 | return -EPERM; |
1974 | 1989 | ||
1975 | switch (cmd) { | 1990 | switch (cmd) { |
@@ -1991,7 +2006,7 @@ do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) | |||
1991 | { | 2006 | { |
1992 | int ret; | 2007 | int ret; |
1993 | 2008 | ||
1994 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) | 2009 | if (!capable(CAP_NET_ADMIN)) |
1995 | return -EPERM; | 2010 | return -EPERM; |
1996 | 2011 | ||
1997 | switch (cmd) { | 2012 | switch (cmd) { |
@@ -2016,7 +2031,7 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) | |||
2016 | { | 2031 | { |
2017 | int ret; | 2032 | int ret; |
2018 | 2033 | ||
2019 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) | 2034 | if (!capable(CAP_NET_ADMIN)) |
2020 | return -EPERM; | 2035 | return -EPERM; |
2021 | 2036 | ||
2022 | switch (cmd) { | 2037 | switch (cmd) { |
@@ -2271,9 +2286,90 @@ static void __exit ip6_tables_fini(void) | |||
2271 | unregister_pernet_subsys(&ip6_tables_net_ops); | 2286 | unregister_pernet_subsys(&ip6_tables_net_ops); |
2272 | } | 2287 | } |
2273 | 2288 | ||
2289 | /* | ||
2290 | * find the offset to specified header or the protocol number of last header | ||
2291 | * if target < 0. "last header" is transport protocol header, ESP, or | ||
2292 | * "No next header". | ||
2293 | * | ||
2294 | * If target header is found, its offset is set in *offset and return protocol | ||
2295 | * number. Otherwise, return -ENOENT or -EBADMSG. | ||
2296 | * | ||
2297 | * If the first fragment doesn't contain the final protocol header or | ||
2298 | * NEXTHDR_NONE it is considered invalid. | ||
2299 | * | ||
2300 | * Note that non-1st fragment is special case that "the protocol number | ||
2301 | * of last header" is "next header" field in Fragment header. In this case, | ||
2302 | * *offset is meaningless. If fragoff is not NULL, the fragment offset is | ||
2303 | * stored in *fragoff; if it is NULL, return -EINVAL. | ||
2304 | */ | ||
2305 | int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, | ||
2306 | int target, unsigned short *fragoff) | ||
2307 | { | ||
2308 | unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); | ||
2309 | u8 nexthdr = ipv6_hdr(skb)->nexthdr; | ||
2310 | unsigned int len = skb->len - start; | ||
2311 | |||
2312 | if (fragoff) | ||
2313 | *fragoff = 0; | ||
2314 | |||
2315 | while (nexthdr != target) { | ||
2316 | struct ipv6_opt_hdr _hdr, *hp; | ||
2317 | unsigned int hdrlen; | ||
2318 | |||
2319 | if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { | ||
2320 | if (target < 0) | ||
2321 | break; | ||
2322 | return -ENOENT; | ||
2323 | } | ||
2324 | |||
2325 | hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); | ||
2326 | if (hp == NULL) | ||
2327 | return -EBADMSG; | ||
2328 | if (nexthdr == NEXTHDR_FRAGMENT) { | ||
2329 | unsigned short _frag_off; | ||
2330 | __be16 *fp; | ||
2331 | fp = skb_header_pointer(skb, | ||
2332 | start+offsetof(struct frag_hdr, | ||
2333 | frag_off), | ||
2334 | sizeof(_frag_off), | ||
2335 | &_frag_off); | ||
2336 | if (fp == NULL) | ||
2337 | return -EBADMSG; | ||
2338 | |||
2339 | _frag_off = ntohs(*fp) & ~0x7; | ||
2340 | if (_frag_off) { | ||
2341 | if (target < 0 && | ||
2342 | ((!ipv6_ext_hdr(hp->nexthdr)) || | ||
2343 | hp->nexthdr == NEXTHDR_NONE)) { | ||
2344 | if (fragoff) { | ||
2345 | *fragoff = _frag_off; | ||
2346 | return hp->nexthdr; | ||
2347 | } else { | ||
2348 | return -EINVAL; | ||
2349 | } | ||
2350 | } | ||
2351 | return -ENOENT; | ||
2352 | } | ||
2353 | hdrlen = 8; | ||
2354 | } else if (nexthdr == NEXTHDR_AUTH) | ||
2355 | hdrlen = (hp->hdrlen + 2) << 2; | ||
2356 | else | ||
2357 | hdrlen = ipv6_optlen(hp); | ||
2358 | |||
2359 | nexthdr = hp->nexthdr; | ||
2360 | len -= hdrlen; | ||
2361 | start += hdrlen; | ||
2362 | } | ||
2363 | |||
2364 | *offset = start; | ||
2365 | return nexthdr; | ||
2366 | } | ||
2367 | |||
2274 | EXPORT_SYMBOL(ip6t_register_table); | 2368 | EXPORT_SYMBOL(ip6t_register_table); |
2275 | EXPORT_SYMBOL(ip6t_unregister_table); | 2369 | EXPORT_SYMBOL(ip6t_unregister_table); |
2276 | EXPORT_SYMBOL(ip6t_do_table); | 2370 | EXPORT_SYMBOL(ip6t_do_table); |
2371 | EXPORT_SYMBOL(ip6t_ext_hdr); | ||
2372 | EXPORT_SYMBOL(ipv6_find_hdr); | ||
2277 | 2373 | ||
2278 | module_init(ip6_tables_init); | 2374 | module_init(ip6_tables_init); |
2279 | module_exit(ip6_tables_fini); | 2375 | module_exit(ip6_tables_fini); |