1/* ipv6header match - matches IPv6 packets based 2 on whether they contain certain headers */ 3 4/* Original idea: Brad Chapman 5 * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */ 6 7/* (C) 2001-2002 Andras Kis-Szabo <kisza@sch.bme.hu> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#include <linux/module.h> 15#include <linux/skbuff.h> 16#include <linux/ipv6.h> 17#include <linux/types.h> 18#include <net/checksum.h> 19#include <net/ipv6.h> 20 21#include <linux/netfilter/x_tables.h> 22#include <linux/netfilter_ipv6/ip6_tables.h> 23#include <linux/netfilter_ipv6/ip6t_ipv6header.h> 24 25MODULE_LICENSE("GPL"); 26MODULE_DESCRIPTION("Xtables: IPv6 header types match"); 27MODULE_AUTHOR("Andras Kis-Szabo <kisza@sch.bme.hu>"); 28 29static bool 30ipv6header_mt6(const struct sk_buff *skb, struct xt_action_param *par) 31{ 32 const struct ip6t_ipv6header_info *info = par->matchinfo; 33 unsigned int temp; 34 int len; 35 u8 nexthdr; 36 unsigned int ptr; 37 38 /* Make sure this isn't an evil packet */ 39 40 /* type of the 1st exthdr */ 41 nexthdr = ipv6_hdr(skb)->nexthdr; 42 /* pointer to the 1st exthdr */ 43 ptr = sizeof(struct ipv6hdr); 44 /* available length */ 45 len = skb->len - ptr; 46 temp = 0; 47 48 while (ip6t_ext_hdr(nexthdr)) { 49 const struct ipv6_opt_hdr *hp; 50 struct ipv6_opt_hdr _hdr; 51 int hdrlen; 52 53 /* No more exthdr -> evaluate */ 54 if (nexthdr == NEXTHDR_NONE) { 55 temp |= MASK_NONE; 56 break; 57 } 58 /* Is there enough space for the next ext header? */ 59 if (len < (int)sizeof(struct ipv6_opt_hdr)) 60 return false; 61 /* ESP -> evaluate */ 62 if (nexthdr == NEXTHDR_ESP) { 63 temp |= MASK_ESP; 64 break; 65 } 66 67 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); 68 BUG_ON(hp == NULL); 69 70 /* Calculate the header length */ 71 if (nexthdr == NEXTHDR_FRAGMENT) 72 hdrlen = 8; 73 else if (nexthdr == NEXTHDR_AUTH) 74 hdrlen = (hp->hdrlen + 2) << 2; 75 else 76 hdrlen = ipv6_optlen(hp); 77 78 /* set the flag */ 79 switch (nexthdr) { 80 case NEXTHDR_HOP: 81 temp |= MASK_HOPOPTS; 82 break; 83 case NEXTHDR_ROUTING: 84 temp |= MASK_ROUTING; 85 break; 86 case NEXTHDR_FRAGMENT: 87 temp |= MASK_FRAGMENT; 88 break; 89 case NEXTHDR_AUTH: 90 temp |= MASK_AH; 91 break; 92 case NEXTHDR_DEST: 93 temp |= MASK_DSTOPTS; 94 break; 95 default: 96 return false; 97 break; 98 } 99 100 nexthdr = hp->nexthdr; 101 len -= hdrlen; 102 ptr += hdrlen; 103 if (ptr > skb->len) 104 break; 105 } 106 107 if (nexthdr != NEXTHDR_NONE && nexthdr != NEXTHDR_ESP) 108 temp |= MASK_PROTO; 109 110 if (info->modeflag) 111 return !((temp ^ info->matchflags ^ info->invflags) 112 & info->matchflags); 113 else { 114 if (info->invflags) 115 return temp != info->matchflags; 116 else 117 return temp == info->matchflags; 118 } 119} 120 121static int ipv6header_mt6_check(const struct xt_mtchk_param *par) 122{ 123 const struct ip6t_ipv6header_info *info = par->matchinfo; 124 125 /* invflags is 0 or 0xff in hard mode */ 126 if ((!info->modeflag) && info->invflags != 0x00 && 127 info->invflags != 0xFF) 128 return -EINVAL; 129 130 return 0; 131} 132 133static struct xt_match ipv6header_mt6_reg __read_mostly = { 134 .name = "ipv6header", 135 .family = NFPROTO_IPV6, 136 .match = ipv6header_mt6, 137 .matchsize = sizeof(struct ip6t_ipv6header_info), 138 .checkentry = ipv6header_mt6_check, 139 .destroy = NULL, 140 .me = THIS_MODULE, 141}; 142 143static int __init ipv6header_mt6_init(void) 144{ 145 return xt_register_match(&ipv6header_mt6_reg); 146} 147 148static void __exit ipv6header_mt6_exit(void) 149{ 150 xt_unregister_match(&ipv6header_mt6_reg); 151} 152 153module_init(ipv6header_mt6_init); 154module_exit(ipv6header_mt6_exit); 155