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 (at) sch.bme.hu> */ 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <netdb.h> 11 #include <xtables.h> 12 #include <linux/netfilter_ipv6/ip6t_ipv6header.h> 13 14 enum { 15 O_HEADER = 0, 16 O_SOFT, 17 }; 18 19 /* A few hardcoded protocols for 'all' and in case the user has no 20 * /etc/protocols */ 21 struct pprot { 22 char *name; 23 uint8_t num; 24 }; 25 26 struct numflag { 27 uint8_t proto; 28 uint8_t flag; 29 }; 30 31 static const struct pprot chain_protos[] = { 32 { "hop-by-hop", IPPROTO_HOPOPTS }, 33 { "protocol", IPPROTO_RAW }, 34 { "hop", IPPROTO_HOPOPTS }, 35 { "dst", IPPROTO_DSTOPTS }, 36 { "route", IPPROTO_ROUTING }, 37 { "frag", IPPROTO_FRAGMENT }, 38 { "auth", IPPROTO_AH }, 39 { "esp", IPPROTO_ESP }, 40 { "none", IPPROTO_NONE }, 41 { "prot", IPPROTO_RAW }, 42 { "0", IPPROTO_HOPOPTS }, 43 { "60", IPPROTO_DSTOPTS }, 44 { "43", IPPROTO_ROUTING }, 45 { "44", IPPROTO_FRAGMENT }, 46 { "51", IPPROTO_AH }, 47 { "50", IPPROTO_ESP }, 48 { "59", IPPROTO_NONE }, 49 { "255", IPPROTO_RAW }, 50 /* { "all", 0 }, */ 51 }; 52 53 static const struct numflag chain_flags[] = { 54 { IPPROTO_HOPOPTS, MASK_HOPOPTS }, 55 { IPPROTO_DSTOPTS, MASK_DSTOPTS }, 56 { IPPROTO_ROUTING, MASK_ROUTING }, 57 { IPPROTO_FRAGMENT, MASK_FRAGMENT }, 58 { IPPROTO_AH, MASK_AH }, 59 { IPPROTO_ESP, MASK_ESP }, 60 { IPPROTO_NONE, MASK_NONE }, 61 { IPPROTO_RAW, MASK_PROTO }, 62 }; 63 64 static const char * 65 proto_to_name(uint8_t proto, int nolookup) 66 { 67 unsigned int i; 68 69 if (proto && !nolookup) { 70 const struct protoent *pent = getprotobynumber(proto); 71 if (pent) 72 return pent->p_name; 73 } 74 75 for (i = 0; i < ARRAY_SIZE(chain_protos); ++i) 76 if (chain_protos[i].num == proto) 77 return chain_protos[i].name; 78 79 return NULL; 80 } 81 82 static uint16_t 83 name_to_proto(const char *s) 84 { 85 unsigned int proto=0; 86 const struct protoent *pent; 87 88 if ((pent = getprotobyname(s))) 89 proto = pent->p_proto; 90 else { 91 unsigned int i; 92 for (i = 0; i < ARRAY_SIZE(chain_protos); ++i) 93 if (strcmp(s, chain_protos[i].name) == 0) { 94 proto = chain_protos[i].num; 95 break; 96 } 97 98 if (i == ARRAY_SIZE(chain_protos)) 99 xtables_error(PARAMETER_PROBLEM, 100 "unknown header `%s' specified", 101 s); 102 } 103 104 return proto; 105 } 106 107 static unsigned int 108 add_proto_to_mask(int proto){ 109 unsigned int i=0, flag=0; 110 111 for (i = 0; i < ARRAY_SIZE(chain_flags); ++i) 112 if (proto == chain_flags[i].proto){ 113 flag = chain_flags[i].flag; 114 break; 115 } 116 117 if (i == ARRAY_SIZE(chain_flags)) 118 xtables_error(PARAMETER_PROBLEM, 119 "unknown header `%d' specified", 120 proto); 121 122 return flag; 123 } 124 125 static void ipv6header_help(void) 126 { 127 printf( 128 "ipv6header match options:\n" 129 "[!] --header headers Type of header to match, by name\n" 130 " names: hop,dst,route,frag,auth,esp,none,proto\n" 131 " long names: hop-by-hop,ipv6-opts,ipv6-route,\n" 132 " ipv6-frag,ah,esp,ipv6-nonxt,protocol\n" 133 " numbers: 0,60,43,44,51,50,59\n" 134 "--soft The header CONTAINS the specified extensions\n"); 135 } 136 137 static const struct xt_option_entry ipv6header_opts[] = { 138 {.name = "header", .id = O_HEADER, .type = XTTYPE_STRING, 139 .flags = XTOPT_MAND | XTOPT_INVERT}, 140 {.name = "soft", .id = O_SOFT, .type = XTTYPE_NONE}, 141 XTOPT_TABLEEND, 142 }; 143 144 static unsigned int 145 parse_header(const char *flags) { 146 unsigned int ret = 0; 147 char *ptr; 148 char *buffer; 149 150 buffer = strdup(flags); 151 152 for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) 153 ret |= add_proto_to_mask(name_to_proto(ptr)); 154 155 free(buffer); 156 return ret; 157 } 158 159 static void ipv6header_parse(struct xt_option_call *cb) 160 { 161 struct ip6t_ipv6header_info *info = cb->data; 162 163 xtables_option_parse(cb); 164 switch (cb->entry->id) { 165 case O_HEADER: 166 if (!(info->matchflags = parse_header(cb->arg))) 167 xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names"); 168 if (cb->invert) 169 info->invflags |= 0xFF; 170 break; 171 case O_SOFT: 172 info->modeflag |= 0xFF; 173 break; 174 } 175 } 176 177 static void 178 print_header(uint8_t flags){ 179 int have_flag = 0; 180 181 while (flags) { 182 unsigned int i; 183 184 for (i = 0; (flags & chain_flags[i].flag) == 0; i++); 185 186 if (have_flag) 187 printf(","); 188 189 printf("%s", proto_to_name(chain_flags[i].proto,0)); 190 have_flag = 1; 191 192 flags &= ~chain_flags[i].flag; 193 } 194 195 if (!have_flag) 196 printf("NONE"); 197 } 198 199 static void ipv6header_print(const void *ip, 200 const struct xt_entry_match *match, int numeric) 201 { 202 const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; 203 printf(" ipv6header"); 204 205 if (info->matchflags || info->invflags) { 206 printf(" flags:%s", info->invflags ? "!" : ""); 207 if (numeric) 208 printf("0x%02X", info->matchflags); 209 else { 210 print_header(info->matchflags); 211 } 212 } 213 214 if (info->modeflag) 215 printf(" soft"); 216 } 217 218 static void ipv6header_save(const void *ip, const struct xt_entry_match *match) 219 { 220 221 const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; 222 223 printf("%s --header ", info->invflags ? " !" : ""); 224 print_header(info->matchflags); 225 if (info->modeflag) 226 printf(" --soft"); 227 } 228 229 static struct xtables_match ipv6header_mt6_reg = { 230 .name = "ipv6header", 231 .version = XTABLES_VERSION, 232 .family = NFPROTO_IPV6, 233 .size = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), 234 .userspacesize = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), 235 .help = ipv6header_help, 236 .print = ipv6header_print, 237 .save = ipv6header_save, 238 .x6_parse = ipv6header_parse, 239 .x6_options = ipv6header_opts, 240 }; 241 242 void _init(void) 243 { 244 xtables_register_match(&ipv6header_mt6_reg); 245 } 246