11da177e4SLinus Torvalds /* ipv6header match - matches IPv6 packets based 21da177e4SLinus Torvalds on whether they contain certain headers */ 31da177e4SLinus Torvalds 41da177e4SLinus Torvalds /* Original idea: Brad Chapman 51da177e4SLinus Torvalds * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds /* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 101da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2 as 111da177e4SLinus Torvalds * published by the Free Software Foundation. 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/module.h> 151da177e4SLinus Torvalds #include <linux/skbuff.h> 161da177e4SLinus Torvalds #include <linux/ipv6.h> 171da177e4SLinus Torvalds #include <linux/types.h> 181da177e4SLinus Torvalds #include <net/checksum.h> 191da177e4SLinus Torvalds #include <net/ipv6.h> 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #include <linux/netfilter_ipv6/ip6_tables.h> 221da177e4SLinus Torvalds #include <linux/netfilter_ipv6/ip6t_ipv6header.h> 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 251da177e4SLinus Torvalds MODULE_DESCRIPTION("IPv6 headers match"); 261da177e4SLinus Torvalds MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds static int 291da177e4SLinus Torvalds ipv6header_match(const struct sk_buff *skb, 301da177e4SLinus Torvalds const struct net_device *in, 311da177e4SLinus Torvalds const struct net_device *out, 32c4986734SPatrick McHardy const struct xt_match *match, 331da177e4SLinus Torvalds const void *matchinfo, 341da177e4SLinus Torvalds int offset, 351da177e4SLinus Torvalds unsigned int protoff, 361da177e4SLinus Torvalds int *hotdrop) 371da177e4SLinus Torvalds { 381da177e4SLinus Torvalds const struct ip6t_ipv6header_info *info = matchinfo; 391da177e4SLinus Torvalds unsigned int temp; 401da177e4SLinus Torvalds int len; 411da177e4SLinus Torvalds u8 nexthdr; 421da177e4SLinus Torvalds unsigned int ptr; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds /* Make sure this isn't an evil packet */ 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds /* type of the 1st exthdr */ 471da177e4SLinus Torvalds nexthdr = skb->nh.ipv6h->nexthdr; 481da177e4SLinus Torvalds /* pointer to the 1st exthdr */ 491da177e4SLinus Torvalds ptr = sizeof(struct ipv6hdr); 501da177e4SLinus Torvalds /* available length */ 511da177e4SLinus Torvalds len = skb->len - ptr; 521da177e4SLinus Torvalds temp = 0; 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds while (ip6t_ext_hdr(nexthdr)) { 551da177e4SLinus Torvalds struct ipv6_opt_hdr _hdr, *hp; 561da177e4SLinus Torvalds int hdrlen; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* Is there enough space for the next ext header? */ 591da177e4SLinus Torvalds if (len < (int)sizeof(struct ipv6_opt_hdr)) 601da177e4SLinus Torvalds return 0; 611da177e4SLinus Torvalds /* No more exthdr -> evaluate */ 621da177e4SLinus Torvalds if (nexthdr == NEXTHDR_NONE) { 631da177e4SLinus Torvalds temp |= MASK_NONE; 641da177e4SLinus Torvalds break; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds /* ESP -> evaluate */ 671da177e4SLinus Torvalds if (nexthdr == NEXTHDR_ESP) { 681da177e4SLinus Torvalds temp |= MASK_ESP; 691da177e4SLinus Torvalds break; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); 731da177e4SLinus Torvalds BUG_ON(hp == NULL); 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds /* Calculate the header length */ 761da177e4SLinus Torvalds if (nexthdr == NEXTHDR_FRAGMENT) { 771da177e4SLinus Torvalds hdrlen = 8; 781da177e4SLinus Torvalds } else if (nexthdr == NEXTHDR_AUTH) 791da177e4SLinus Torvalds hdrlen = (hp->hdrlen + 2) << 2; 801da177e4SLinus Torvalds else 811da177e4SLinus Torvalds hdrlen = ipv6_optlen(hp); 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds /* set the flag */ 841da177e4SLinus Torvalds switch (nexthdr) { 851da177e4SLinus Torvalds case NEXTHDR_HOP: 861da177e4SLinus Torvalds temp |= MASK_HOPOPTS; 871da177e4SLinus Torvalds break; 881da177e4SLinus Torvalds case NEXTHDR_ROUTING: 891da177e4SLinus Torvalds temp |= MASK_ROUTING; 901da177e4SLinus Torvalds break; 911da177e4SLinus Torvalds case NEXTHDR_FRAGMENT: 921da177e4SLinus Torvalds temp |= MASK_FRAGMENT; 931da177e4SLinus Torvalds break; 941da177e4SLinus Torvalds case NEXTHDR_AUTH: 951da177e4SLinus Torvalds temp |= MASK_AH; 961da177e4SLinus Torvalds break; 971da177e4SLinus Torvalds case NEXTHDR_DEST: 981da177e4SLinus Torvalds temp |= MASK_DSTOPTS; 991da177e4SLinus Torvalds break; 1001da177e4SLinus Torvalds default: 1011da177e4SLinus Torvalds return 0; 1021da177e4SLinus Torvalds break; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds nexthdr = hp->nexthdr; 1061da177e4SLinus Torvalds len -= hdrlen; 1071da177e4SLinus Torvalds ptr += hdrlen; 1081da177e4SLinus Torvalds if (ptr > skb->len) 1091da177e4SLinus Torvalds break; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds if ((nexthdr != NEXTHDR_NONE) && (nexthdr != NEXTHDR_ESP)) 1131da177e4SLinus Torvalds temp |= MASK_PROTO; 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds if (info->modeflag) 1161da177e4SLinus Torvalds return !((temp ^ info->matchflags ^ info->invflags) 1171da177e4SLinus Torvalds & info->matchflags); 1181da177e4SLinus Torvalds else { 1191da177e4SLinus Torvalds if (info->invflags) 1201da177e4SLinus Torvalds return temp != info->matchflags; 1211da177e4SLinus Torvalds else 1221da177e4SLinus Torvalds return temp == info->matchflags; 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds static int 1271da177e4SLinus Torvalds ipv6header_checkentry(const char *tablename, 1282e4e6a17SHarald Welte const void *ip, 129c4986734SPatrick McHardy const struct xt_match *match, 1301da177e4SLinus Torvalds void *matchinfo, 1311da177e4SLinus Torvalds unsigned int matchsize, 1321da177e4SLinus Torvalds unsigned int hook_mask) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds const struct ip6t_ipv6header_info *info = matchinfo; 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds /* invflags is 0 or 0xff in hard mode */ 137f0daaa65SYasuyuki Kozakai if ((!info->modeflag) && info->invflags != 0x00 && 138f0daaa65SYasuyuki Kozakai info->invflags != 0xFF) 1391da177e4SLinus Torvalds return 0; 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds return 1; 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds static struct ip6t_match ip6t_ipv6header_match = { 1451da177e4SLinus Torvalds .name = "ipv6header", 1461da177e4SLinus Torvalds .match = &ipv6header_match, 1477f939713SPatrick McHardy .matchsize = sizeof(struct ip6t_ipv6header_info), 1481da177e4SLinus Torvalds .checkentry = &ipv6header_checkentry, 1491da177e4SLinus Torvalds .destroy = NULL, 1501da177e4SLinus Torvalds .me = THIS_MODULE, 1511da177e4SLinus Torvalds }; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds static int __init ipv6header_init(void) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds return ip6t_register_match(&ip6t_ipv6header_match); 1561da177e4SLinus Torvalds } 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds static void __exit ipv6header_exit(void) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds ip6t_unregister_match(&ip6t_ipv6header_match); 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds module_init(ipv6header_init); 1641da177e4SLinus Torvalds module_exit(ipv6header_exit); 165