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 } 98 99 nexthdr = hp->nexthdr; 100 len -= hdrlen; 101 ptr += hdrlen; 102 if (ptr > skb->len) 103 break; 104 } 105 106 if (nexthdr != NEXTHDR_NONE && nexthdr != NEXTHDR_ESP) 107 temp |= MASK_PROTO; 108 109 if (info->modeflag) 110 return !((temp ^ info->matchflags ^ info->invflags) 111 & info->matchflags); 112 else { 113 if (info->invflags) 114 return temp != info->matchflags; 115 else 116 return temp == info->matchflags; 117 } 118} 119 120static int ipv6header_mt6_check(const struct xt_mtchk_param *par) 121{ 122 const struct ip6t_ipv6header_info *info = par->matchinfo; 123 124 /* invflags is 0 or 0xff in hard mode */ 125 if ((!info->modeflag) && info->invflags != 0x00 && 126 info->invflags != 0xFF) 127 return -EINVAL; 128 129 return 0; 130} 131 132static struct xt_match ipv6header_mt6_reg __read_mostly = { 133 .name = "ipv6header", 134 .family = NFPROTO_IPV6, 135 .match = ipv6header_mt6, 136 .matchsize = sizeof(struct ip6t_ipv6header_info), 137 .checkentry = ipv6header_mt6_check, 138 .destroy = NULL, 139 .me = THIS_MODULE, 140}; 141 142static int __init ipv6header_mt6_init(void) 143{ 144 return xt_register_match(&ipv6header_mt6_reg); 145} 146 147static void __exit ipv6header_mt6_exit(void) 148{ 149 xt_unregister_match(&ipv6header_mt6_reg); 150} 151 152module_init(ipv6header_mt6_init); 153module_exit(ipv6header_mt6_exit); 154