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, 321da177e4SLinus Torvalds const void *matchinfo, 331da177e4SLinus Torvalds int offset, 341da177e4SLinus Torvalds unsigned int protoff, 351da177e4SLinus Torvalds int *hotdrop) 361da177e4SLinus Torvalds { 371da177e4SLinus Torvalds const struct ip6t_ipv6header_info *info = matchinfo; 381da177e4SLinus Torvalds unsigned int temp; 391da177e4SLinus Torvalds int len; 401da177e4SLinus Torvalds u8 nexthdr; 411da177e4SLinus Torvalds unsigned int ptr; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds /* Make sure this isn't an evil packet */ 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* type of the 1st exthdr */ 461da177e4SLinus Torvalds nexthdr = skb->nh.ipv6h->nexthdr; 471da177e4SLinus Torvalds /* pointer to the 1st exthdr */ 481da177e4SLinus Torvalds ptr = sizeof(struct ipv6hdr); 491da177e4SLinus Torvalds /* available length */ 501da177e4SLinus Torvalds len = skb->len - ptr; 511da177e4SLinus Torvalds temp = 0; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds while (ip6t_ext_hdr(nexthdr)) { 541da177e4SLinus Torvalds struct ipv6_opt_hdr _hdr, *hp; 551da177e4SLinus Torvalds int hdrlen; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds /* Is there enough space for the next ext header? */ 581da177e4SLinus Torvalds if (len < (int)sizeof(struct ipv6_opt_hdr)) 591da177e4SLinus Torvalds return 0; 601da177e4SLinus Torvalds /* No more exthdr -> evaluate */ 611da177e4SLinus Torvalds if (nexthdr == NEXTHDR_NONE) { 621da177e4SLinus Torvalds temp |= MASK_NONE; 631da177e4SLinus Torvalds break; 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds /* ESP -> evaluate */ 661da177e4SLinus Torvalds if (nexthdr == NEXTHDR_ESP) { 671da177e4SLinus Torvalds temp |= MASK_ESP; 681da177e4SLinus Torvalds break; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); 721da177e4SLinus Torvalds BUG_ON(hp == NULL); 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* Calculate the header length */ 751da177e4SLinus Torvalds if (nexthdr == NEXTHDR_FRAGMENT) { 761da177e4SLinus Torvalds hdrlen = 8; 771da177e4SLinus Torvalds } else if (nexthdr == NEXTHDR_AUTH) 781da177e4SLinus Torvalds hdrlen = (hp->hdrlen+2)<<2; 791da177e4SLinus Torvalds else 801da177e4SLinus Torvalds hdrlen = ipv6_optlen(hp); 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds /* set the flag */ 831da177e4SLinus Torvalds switch (nexthdr){ 841da177e4SLinus Torvalds case NEXTHDR_HOP: 851da177e4SLinus Torvalds temp |= MASK_HOPOPTS; 861da177e4SLinus Torvalds break; 871da177e4SLinus Torvalds case NEXTHDR_ROUTING: 881da177e4SLinus Torvalds temp |= MASK_ROUTING; 891da177e4SLinus Torvalds break; 901da177e4SLinus Torvalds case NEXTHDR_FRAGMENT: 911da177e4SLinus Torvalds temp |= MASK_FRAGMENT; 921da177e4SLinus Torvalds break; 931da177e4SLinus Torvalds case NEXTHDR_AUTH: 941da177e4SLinus Torvalds temp |= MASK_AH; 951da177e4SLinus Torvalds break; 961da177e4SLinus Torvalds case NEXTHDR_DEST: 971da177e4SLinus Torvalds temp |= MASK_DSTOPTS; 981da177e4SLinus Torvalds break; 991da177e4SLinus Torvalds default: 1001da177e4SLinus Torvalds return 0; 1011da177e4SLinus Torvalds break; 1021da177e4SLinus Torvalds } 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds nexthdr = hp->nexthdr; 1051da177e4SLinus Torvalds len -= hdrlen; 1061da177e4SLinus Torvalds ptr += hdrlen; 1071da177e4SLinus Torvalds if (ptr > skb->len) 1081da177e4SLinus Torvalds break; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds if ( (nexthdr != NEXTHDR_NONE ) && (nexthdr != NEXTHDR_ESP) ) 1121da177e4SLinus Torvalds temp |= MASK_PROTO; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds if (info->modeflag) 1151da177e4SLinus Torvalds return !((temp ^ info->matchflags ^ info->invflags) 1161da177e4SLinus Torvalds & info->matchflags); 1171da177e4SLinus Torvalds else { 1181da177e4SLinus Torvalds if (info->invflags) 1191da177e4SLinus Torvalds return temp != info->matchflags; 1201da177e4SLinus Torvalds else 1211da177e4SLinus Torvalds return temp == info->matchflags; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds static int 1261da177e4SLinus Torvalds ipv6header_checkentry(const char *tablename, 1271da177e4SLinus Torvalds const struct ip6t_ip6 *ip, 1281da177e4SLinus Torvalds void *matchinfo, 1291da177e4SLinus Torvalds unsigned int matchsize, 1301da177e4SLinus Torvalds unsigned int hook_mask) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds const struct ip6t_ipv6header_info *info = matchinfo; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds /* Check for obvious errors */ 1351da177e4SLinus Torvalds /* This match is valid in all hooks! */ 1361da177e4SLinus Torvalds if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info))) 1371da177e4SLinus Torvalds return 0; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* invflags is 0 or 0xff in hard mode */ 1401da177e4SLinus Torvalds if ((!info->modeflag) && info->invflags != 0x00 1411da177e4SLinus Torvalds && info->invflags != 0xFF) 1421da177e4SLinus Torvalds return 0; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds return 1; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct ip6t_match ip6t_ipv6header_match = { 1481da177e4SLinus Torvalds .name = "ipv6header", 1491da177e4SLinus Torvalds .match = &ipv6header_match, 1501da177e4SLinus Torvalds .checkentry = &ipv6header_checkentry, 1511da177e4SLinus Torvalds .destroy = NULL, 1521da177e4SLinus Torvalds .me = THIS_MODULE, 1531da177e4SLinus Torvalds }; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds static int __init ipv6header_init(void) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds return ip6t_register_match(&ip6t_ipv6header_match); 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds static void __exit ipv6header_exit(void) 1611da177e4SLinus Torvalds { 1621da177e4SLinus Torvalds ip6t_unregister_match(&ip6t_ipv6header_match); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds module_init(ipv6header_init); 1661da177e4SLinus Torvalds module_exit(ipv6header_exit); 1671da177e4SLinus Torvalds 168