Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: ip6_tables: add flags parameter to ipv6_find_hdr()

This patch adds the flags parameter to ipv6_find_hdr. This flags
allows us to:

* know if this is a fragment.
* stop at the AH header, so the information contained in that header
can be used for some specific packet handling.

This patch also adds the offset parameter for inspection of one
inner IPv6 header that is contained in error messages.

Signed-off-by: Hans Schillstrom <hans.schillstrom@ericsson.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Hans Schillstrom and committed by
Pablo Neira Ayuso
84018f55 9bb862be

+49 -18
+6 -1
include/linux/netfilter_ipv6/ip6_tables.h
··· 298 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 306 /* find specified header and get offset to it */ 302 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 310 #ifdef CONFIG_COMPAT 306 311 #include <net/compat.h>
+31 -5
net/ipv6/netfilter/ip6_tables.c
··· 133 133 int protohdr; 134 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 137 if (protohdr < 0) { 138 138 if (_frag_off == 0) 139 139 *hotdrop = true; ··· 362 362 const struct xt_entry_match *ematch; 363 363 364 364 IP_NF_ASSERT(e); 365 + acpar.thoff = 0; 365 366 if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, 366 367 &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { 367 368 no_match: ··· 2279 2278 * if target < 0. "last header" is transport protocol header, ESP, or 2280 2279 * "No next header". 2281 2280 * 2281 + * Note that *offset is used as input/output parameter. an if it is not zero, 2282 + * then it must be a valid offset to an inner IPv6 header. This can be used 2283 + * to explore inner IPv6 header, eg. ICMPv6 error messages. 2284 + * 2282 2285 * If target header is found, its offset is set in *offset and return protocol 2283 2286 * number. Otherwise, return -1. 2284 2287 * ··· 2294 2289 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff 2295 2290 * isn't NULL. 2296 2291 * 2292 + * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG 2293 + * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and 2294 + * target < 0, then this function will stop at the AH header. 2297 2295 */ 2298 2296 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, 2299 - int target, unsigned short *fragoff) 2297 + int target, unsigned short *fragoff, int *flags) 2300 2298 { 2301 2299 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); 2302 2300 u8 nexthdr = ipv6_hdr(skb)->nexthdr; 2303 - unsigned int len = skb->len - start; 2301 + unsigned int len; 2304 2302 2305 2303 if (fragoff) 2306 2304 *fragoff = 0; 2305 + 2306 + if (*offset) { 2307 + struct ipv6hdr _ip6, *ip6; 2308 + 2309 + ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); 2310 + if (!ip6 || (ip6->version != 6)) { 2311 + printk(KERN_ERR "IPv6 header not found\n"); 2312 + return -EBADMSG; 2313 + } 2314 + start = *offset + sizeof(struct ipv6hdr); 2315 + nexthdr = ip6->nexthdr; 2316 + } 2317 + len = skb->len - start; 2307 2318 2308 2319 while (nexthdr != target) { 2309 2320 struct ipv6_opt_hdr _hdr, *hp; ··· 2337 2316 if (nexthdr == NEXTHDR_FRAGMENT) { 2338 2317 unsigned short _frag_off; 2339 2318 __be16 *fp; 2319 + 2320 + if (flags) /* Indicate that this is a fragment */ 2321 + *flags |= IP6T_FH_F_FRAG; 2340 2322 fp = skb_header_pointer(skb, 2341 2323 start+offsetof(struct frag_hdr, 2342 2324 frag_off), ··· 2360 2336 return -ENOENT; 2361 2337 } 2362 2338 hdrlen = 8; 2363 - } else if (nexthdr == NEXTHDR_AUTH) 2339 + } else if (nexthdr == NEXTHDR_AUTH) { 2340 + if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) 2341 + break; 2364 2342 hdrlen = (hp->hdrlen + 2) << 2; 2365 - else 2343 + } else 2366 2344 hdrlen = ipv6_optlen(hp); 2367 2345 2368 2346 nexthdr = hp->nexthdr;
+2 -2
net/ipv6/netfilter/ip6t_ah.c
··· 41 41 struct ip_auth_hdr _ah; 42 42 const struct ip_auth_hdr *ah; 43 43 const struct ip6t_ah *ahinfo = par->matchinfo; 44 - unsigned int ptr; 44 + unsigned int ptr = 0; 45 45 unsigned int hdrlen = 0; 46 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 49 if (err < 0) { 50 50 if (err != -ENOENT) 51 51 par->hotdrop = true;
+2 -2
net/ipv6/netfilter/ip6t_frag.c
··· 40 40 struct frag_hdr _frag; 41 41 const struct frag_hdr *fh; 42 42 const struct ip6t_frag *fraginfo = par->matchinfo; 43 - unsigned int ptr; 43 + unsigned int ptr = 0; 44 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 47 if (err < 0) { 48 48 if (err != -ENOENT) 49 49 par->hotdrop = true;
+2 -2
net/ipv6/netfilter/ip6t_hbh.c
··· 50 50 const struct ipv6_opt_hdr *oh; 51 51 const struct ip6t_opts *optinfo = par->matchinfo; 52 52 unsigned int temp; 53 - unsigned int ptr; 53 + unsigned int ptr = 0; 54 54 unsigned int hdrlen = 0; 55 55 bool ret = false; 56 56 u8 _opttype; ··· 62 62 63 63 err = ipv6_find_hdr(skb, &ptr, 64 64 (par->match == &hbh_mt6_reg[0]) ? 65 - NEXTHDR_HOP : NEXTHDR_DEST, NULL); 65 + NEXTHDR_HOP : NEXTHDR_DEST, NULL, NULL); 66 66 if (err < 0) { 67 67 if (err != -ENOENT) 68 68 par->hotdrop = true;
+2 -2
net/ipv6/netfilter/ip6t_rt.c
··· 42 42 const struct ipv6_rt_hdr *rh; 43 43 const struct ip6t_rt *rtinfo = par->matchinfo; 44 44 unsigned int temp; 45 - unsigned int ptr; 45 + unsigned int ptr = 0; 46 46 unsigned int hdrlen = 0; 47 47 bool ret = false; 48 48 struct in6_addr _addr; 49 49 const struct in6_addr *ap; 50 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 53 if (err < 0) { 54 54 if (err != -ENOENT) 55 55 par->hotdrop = true;
+2 -2
net/netfilter/xt_TPROXY.c
··· 282 282 struct sock *sk; 283 283 const struct in6_addr *laddr; 284 284 __be16 lport; 285 - int thoff; 285 + int thoff = 0; 286 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 289 if (tproto < 0) { 290 290 pr_debug("unable to find transport header in IPv6 packet, dropping\n"); 291 291 return NF_DROP;
+2 -2
net/netfilter/xt_socket.c
··· 263 263 struct sock *sk; 264 264 struct in6_addr *daddr, *saddr; 265 265 __be16 dport, sport; 266 - int thoff, tproto; 266 + int thoff = 0, tproto; 267 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 270 if (tproto < 0) { 271 271 pr_debug("unable to find transport header in IPv6 packet, dropping\n"); 272 272 return NF_DROP;