| /* ipv6header match - matches IPv6 packets based |
| on whether they contain certain headers */ |
| |
| /* Original idea: Brad Chapman |
| * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */ |
| |
| #include <getopt.h> |
| #include <xtables.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <netdb.h> |
| #include <sys/types.h> |
| |
| #include <linux/netfilter_ipv6/ip6_tables.h> |
| #include <linux/netfilter_ipv6/ip6t_ipv6header.h> |
| |
| /* This maybe required |
| #include <linux/in.h> |
| #include <linux/in6.h> |
| */ |
| |
| |
| /* A few hardcoded protocols for 'all' and in case the user has no |
| * /etc/protocols */ |
| struct pprot { |
| char *name; |
| u_int8_t num; |
| }; |
| |
| struct numflag { |
| u_int8_t proto; |
| u_int8_t flag; |
| }; |
| |
| static const struct pprot chain_protos[] = { |
| { "hop-by-hop", IPPROTO_HOPOPTS }, |
| { "protocol", IPPROTO_RAW }, |
| { "hop", IPPROTO_HOPOPTS }, |
| { "dst", IPPROTO_DSTOPTS }, |
| { "route", IPPROTO_ROUTING }, |
| { "frag", IPPROTO_FRAGMENT }, |
| { "auth", IPPROTO_AH }, |
| { "esp", IPPROTO_ESP }, |
| { "none", IPPROTO_NONE }, |
| { "prot", IPPROTO_RAW }, |
| { "0", IPPROTO_HOPOPTS }, |
| { "60", IPPROTO_DSTOPTS }, |
| { "43", IPPROTO_ROUTING }, |
| { "44", IPPROTO_FRAGMENT }, |
| { "51", IPPROTO_AH }, |
| { "50", IPPROTO_ESP }, |
| { "59", IPPROTO_NONE }, |
| { "255", IPPROTO_RAW }, |
| /* { "all", 0 }, */ |
| }; |
| |
| static const struct numflag chain_flags[] = { |
| { IPPROTO_HOPOPTS, MASK_HOPOPTS }, |
| { IPPROTO_DSTOPTS, MASK_DSTOPTS }, |
| { IPPROTO_ROUTING, MASK_ROUTING }, |
| { IPPROTO_FRAGMENT, MASK_FRAGMENT }, |
| { IPPROTO_AH, MASK_AH }, |
| { IPPROTO_ESP, MASK_ESP }, |
| { IPPROTO_NONE, MASK_NONE }, |
| { IPPROTO_RAW, MASK_PROTO }, |
| }; |
| |
| static char * |
| proto_to_name(u_int8_t proto, int nolookup) |
| { |
| unsigned int i; |
| |
| if (proto && !nolookup) { |
| struct protoent *pent = getprotobynumber(proto); |
| if (pent) |
| return pent->p_name; |
| } |
| |
| for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) |
| if (chain_protos[i].num == proto) |
| return chain_protos[i].name; |
| |
| return NULL; |
| } |
| |
| static u_int16_t |
| name_to_proto(const char *s) |
| { |
| unsigned int proto=0; |
| struct protoent *pent; |
| |
| if ((pent = getprotobyname(s))) |
| proto = pent->p_proto; |
| else { |
| unsigned int i; |
| for (i = 0; |
| i < sizeof(chain_protos)/sizeof(struct pprot); |
| i++) { |
| if (strcmp(s, chain_protos[i].name) == 0) { |
| proto = chain_protos[i].num; |
| break; |
| } |
| } |
| |
| if (i == sizeof(chain_protos)/sizeof(struct pprot)) |
| xtables_error(PARAMETER_PROBLEM, |
| "unknown header `%s' specified", |
| s); |
| } |
| |
| return proto; |
| } |
| |
| static unsigned int |
| add_proto_to_mask(int proto){ |
| unsigned int i=0, flag=0; |
| |
| for (i = 0; |
| i < sizeof(chain_flags)/sizeof(struct numflag); |
| i++) { |
| if (proto == chain_flags[i].proto){ |
| flag = chain_flags[i].flag; |
| break; |
| } |
| } |
| |
| if (i == sizeof(chain_flags)/sizeof(struct numflag)) |
| xtables_error(PARAMETER_PROBLEM, |
| "unknown header `%d' specified", |
| proto); |
| |
| return flag; |
| } |
| |
| static void ipv6header_help(void) |
| { |
| printf( |
| "ipv6header match options:\n" |
| "[!] --header headers Type of header to match, by name\n" |
| " names: hop,dst,route,frag,auth,esp,none,proto\n" |
| " long names: hop-by-hop,ipv6-opts,ipv6-route,\n" |
| " ipv6-frag,ah,esp,ipv6-nonxt,protocol\n" |
| " numbers: 0,60,43,44,51,50,59\n" |
| "--soft The header CONTAINS the specified extensions\n"); |
| } |
| |
| static const struct option ipv6header_opts[] = { |
| { "header", 1, NULL, '1' }, |
| { "soft", 0, NULL, '2' }, |
| { .name = NULL } |
| }; |
| |
| static void ipv6header_init(struct xt_entry_match *m) |
| { |
| struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)m->data; |
| |
| info->matchflags = 0x00; |
| info->invflags = 0x00; |
| info->modeflag = 0x00; |
| } |
| |
| static unsigned int |
| parse_header(const char *flags) { |
| unsigned int ret = 0; |
| char *ptr; |
| char *buffer; |
| |
| buffer = strdup(flags); |
| |
| for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) |
| ret |= add_proto_to_mask(name_to_proto(ptr)); |
| |
| free(buffer); |
| return ret; |
| } |
| |
| #define IPV6_HDR_HEADER 0x01 |
| #define IPV6_HDR_SOFT 0x02 |
| |
| static int |
| ipv6header_parse(int c, char **argv, int invert, unsigned int *flags, |
| const void *entry, struct xt_entry_match **match) |
| { |
| struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)(*match)->data; |
| |
| switch (c) { |
| case '1' : |
| /* Parse the provided header names */ |
| if (*flags & IPV6_HDR_HEADER) |
| xtables_error(PARAMETER_PROBLEM, |
| "Only one `--header' allowed"); |
| |
| xtables_check_inverse(optarg, &invert, &optind, 0); |
| |
| if (! (info->matchflags = parse_header(argv[optind-1])) ) |
| xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names"); |
| |
| if (invert) |
| info->invflags |= 0xFF; |
| *flags |= IPV6_HDR_HEADER; |
| break; |
| case '2' : |
| /* Soft-mode requested? */ |
| if (*flags & IPV6_HDR_SOFT) |
| xtables_error(PARAMETER_PROBLEM, |
| "Only one `--soft' allowed"); |
| |
| info->modeflag |= 0xFF; |
| *flags |= IPV6_HDR_SOFT; |
| break; |
| default: |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void ipv6header_check(unsigned int flags) |
| { |
| if (!flags) xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: no options specified"); |
| } |
| |
| static void |
| print_header(u_int8_t flags){ |
| int have_flag = 0; |
| |
| while (flags) { |
| unsigned int i; |
| |
| for (i = 0; (flags & chain_flags[i].flag) == 0; i++); |
| |
| if (have_flag) |
| printf(","); |
| |
| printf("%s", proto_to_name(chain_flags[i].proto,0)); |
| have_flag = 1; |
| |
| flags &= ~chain_flags[i].flag; |
| } |
| |
| if (!have_flag) |
| printf("NONE"); |
| } |
| |
| static void ipv6header_print(const void *ip, |
| const struct xt_entry_match *match, int numeric) |
| { |
| const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; |
| printf("ipv6header "); |
| |
| if (info->matchflags || info->invflags) { |
| printf("flags:%s", info->invflags ? "!" : ""); |
| if (numeric) |
| printf("0x%02X ", info->matchflags); |
| else { |
| print_header(info->matchflags); |
| printf(" "); |
| } |
| } |
| |
| if (info->modeflag) |
| printf("soft "); |
| } |
| |
| static void ipv6header_save(const void *ip, const struct xt_entry_match *match) |
| { |
| |
| const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; |
| |
| printf("%s--header ", info->invflags ? "! " : ""); |
| print_header(info->matchflags); |
| printf(" "); |
| if (info->modeflag) |
| printf("--soft "); |
| } |
| |
| static struct xtables_match ipv6header_mt6_reg = { |
| .name = "ipv6header", |
| .version = XTABLES_VERSION, |
| .family = NFPROTO_IPV6, |
| .size = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), |
| .userspacesize = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), |
| .help = ipv6header_help, |
| .init = ipv6header_init, |
| .parse = ipv6header_parse, |
| .final_check = ipv6header_check, |
| .print = ipv6header_print, |
| .save = ipv6header_save, |
| .extra_opts = ipv6header_opts, |
| }; |
| |
| void _init(void) |
| { |
| xtables_register_match(&ipv6header_mt6_reg); |
| } |