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 216709dbbbSJan Engelhardt #include <linux/netfilter/x_tables.h> 221da177e4SLinus Torvalds #include <linux/netfilter_ipv6/ip6_tables.h> 231da177e4SLinus Torvalds #include <linux/netfilter_ipv6/ip6t_ipv6header.h> 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 261da177e4SLinus Torvalds MODULE_DESCRIPTION("IPv6 headers match"); 271da177e4SLinus Torvalds MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds static int 301da177e4SLinus Torvalds ipv6header_match(const struct sk_buff *skb, 311da177e4SLinus Torvalds const struct net_device *in, 321da177e4SLinus Torvalds const struct net_device *out, 33c4986734SPatrick McHardy const struct xt_match *match, 341da177e4SLinus Torvalds const void *matchinfo, 351da177e4SLinus Torvalds int offset, 361da177e4SLinus Torvalds unsigned int protoff, 37cff533acSJan Engelhardt bool *hotdrop) 381da177e4SLinus Torvalds { 391da177e4SLinus Torvalds const struct ip6t_ipv6header_info *info = matchinfo; 401da177e4SLinus Torvalds unsigned int temp; 411da177e4SLinus Torvalds int len; 421da177e4SLinus Torvalds u8 nexthdr; 431da177e4SLinus Torvalds unsigned int ptr; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds /* Make sure this isn't an evil packet */ 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds /* type of the 1st exthdr */ 480660e03fSArnaldo Carvalho de Melo nexthdr = ipv6_hdr(skb)->nexthdr; 491da177e4SLinus Torvalds /* pointer to the 1st exthdr */ 501da177e4SLinus Torvalds ptr = sizeof(struct ipv6hdr); 511da177e4SLinus Torvalds /* available length */ 521da177e4SLinus Torvalds len = skb->len - ptr; 531da177e4SLinus Torvalds temp = 0; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds while (ip6t_ext_hdr(nexthdr)) { 561da177e4SLinus Torvalds struct ipv6_opt_hdr _hdr, *hp; 571da177e4SLinus Torvalds int hdrlen; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds /* Is there enough space for the next ext header? */ 601da177e4SLinus Torvalds if (len < (int)sizeof(struct ipv6_opt_hdr)) 611da177e4SLinus Torvalds return 0; 621da177e4SLinus Torvalds /* No more exthdr -> evaluate */ 631da177e4SLinus Torvalds if (nexthdr == NEXTHDR_NONE) { 641da177e4SLinus Torvalds temp |= MASK_NONE; 651da177e4SLinus Torvalds break; 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds /* ESP -> evaluate */ 681da177e4SLinus Torvalds if (nexthdr == NEXTHDR_ESP) { 691da177e4SLinus Torvalds temp |= MASK_ESP; 701da177e4SLinus Torvalds break; 711da177e4SLinus Torvalds } 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); 741da177e4SLinus Torvalds BUG_ON(hp == NULL); 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /* Calculate the header length */ 771da177e4SLinus Torvalds if (nexthdr == NEXTHDR_FRAGMENT) { 781da177e4SLinus Torvalds hdrlen = 8; 791da177e4SLinus Torvalds } else if (nexthdr == NEXTHDR_AUTH) 801da177e4SLinus Torvalds hdrlen = (hp->hdrlen + 2) << 2; 811da177e4SLinus Torvalds else 821da177e4SLinus Torvalds hdrlen = ipv6_optlen(hp); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds /* set the flag */ 851da177e4SLinus Torvalds switch (nexthdr) { 861da177e4SLinus Torvalds case NEXTHDR_HOP: 871da177e4SLinus Torvalds temp |= MASK_HOPOPTS; 881da177e4SLinus Torvalds break; 891da177e4SLinus Torvalds case NEXTHDR_ROUTING: 901da177e4SLinus Torvalds temp |= MASK_ROUTING; 911da177e4SLinus Torvalds break; 921da177e4SLinus Torvalds case NEXTHDR_FRAGMENT: 931da177e4SLinus Torvalds temp |= MASK_FRAGMENT; 941da177e4SLinus Torvalds break; 951da177e4SLinus Torvalds case NEXTHDR_AUTH: 961da177e4SLinus Torvalds temp |= MASK_AH; 971da177e4SLinus Torvalds break; 981da177e4SLinus Torvalds case NEXTHDR_DEST: 991da177e4SLinus Torvalds temp |= MASK_DSTOPTS; 1001da177e4SLinus Torvalds break; 1011da177e4SLinus Torvalds default: 1021da177e4SLinus Torvalds return 0; 1031da177e4SLinus Torvalds break; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds nexthdr = hp->nexthdr; 1071da177e4SLinus Torvalds len -= hdrlen; 1081da177e4SLinus Torvalds ptr += hdrlen; 1091da177e4SLinus Torvalds if (ptr > skb->len) 1101da177e4SLinus Torvalds break; 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if ((nexthdr != NEXTHDR_NONE) && (nexthdr != NEXTHDR_ESP)) 1141da177e4SLinus Torvalds temp |= MASK_PROTO; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds if (info->modeflag) 1171da177e4SLinus Torvalds return !((temp ^ info->matchflags ^ info->invflags) 1181da177e4SLinus Torvalds & info->matchflags); 1191da177e4SLinus Torvalds else { 1201da177e4SLinus Torvalds if (info->invflags) 1211da177e4SLinus Torvalds return temp != info->matchflags; 1221da177e4SLinus Torvalds else 1231da177e4SLinus Torvalds return temp == info->matchflags; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds static int 1281da177e4SLinus Torvalds ipv6header_checkentry(const char *tablename, 1292e4e6a17SHarald Welte const void *ip, 130c4986734SPatrick McHardy const struct xt_match *match, 1311da177e4SLinus Torvalds void *matchinfo, 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 1446709dbbbSJan Engelhardt static struct xt_match ip6t_ipv6header_match = { 1451da177e4SLinus Torvalds .name = "ipv6header", 1466709dbbbSJan Engelhardt .family = AF_INET6, 1471da177e4SLinus Torvalds .match = &ipv6header_match, 1487f939713SPatrick McHardy .matchsize = sizeof(struct ip6t_ipv6header_info), 1491da177e4SLinus Torvalds .checkentry = &ipv6header_checkentry, 1501da177e4SLinus Torvalds .destroy = NULL, 1511da177e4SLinus Torvalds .me = THIS_MODULE, 1521da177e4SLinus Torvalds }; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds static int __init ipv6header_init(void) 1551da177e4SLinus Torvalds { 1566709dbbbSJan Engelhardt return xt_register_match(&ip6t_ipv6header_match); 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds static void __exit ipv6header_exit(void) 1601da177e4SLinus Torvalds { 1616709dbbbSJan Engelhardt xt_unregister_match(&ip6t_ipv6header_match); 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds module_init(ipv6header_init); 1651da177e4SLinus Torvalds module_exit(ipv6header_exit); 166