/* ebt_ip6 * * Authors: * Kuo-Lang Tseng * Manohar Castelino * * Summary: * This is just a modification of the IPv4 code written by * Bart De Schuymer * with the changes required to support IPv6 * */ #include #include #include #include #include #include #include #include #include #include #include #include "libxt_icmp.h" #define IP_SOURCE '1' #define IP_DEST '2' #define IP_TCLASS '3' #define IP_PROTO '4' #define IP_SPORT '5' #define IP_DPORT '6' #define IP_ICMP6 '7' static const struct option brip6_opts[] = { { .name = "ip6-source", .has_arg = true, .val = IP_SOURCE }, { .name = "ip6-src", .has_arg = true, .val = IP_SOURCE }, { .name = "ip6-destination", .has_arg = true, .val = IP_DEST }, { .name = "ip6-dst", .has_arg = true, .val = IP_DEST }, { .name = "ip6-tclass", .has_arg = true, .val = IP_TCLASS }, { .name = "ip6-protocol", .has_arg = true, .val = IP_PROTO }, { .name = "ip6-proto", .has_arg = true, .val = IP_PROTO }, { .name = "ip6-source-port", .has_arg = true, .val = IP_SPORT }, { .name = "ip6-sport", .has_arg = true, .val = IP_SPORT }, { .name = "ip6-destination-port",.has_arg = true,.val = IP_DPORT }, { .name = "ip6-dport", .has_arg = true, .val = IP_DPORT }, { .name = "ip6-icmp-type", .has_arg = true, .val = IP_ICMP6 }, XT_GETOPT_TABLEEND, }; static const struct xt_icmp_names icmpv6_codes[] = { { "destination-unreachable", 1, 0, 0xFF }, { "no-route", 1, 0, 0 }, { "communication-prohibited", 1, 1, 1 }, { "address-unreachable", 1, 3, 3 }, { "port-unreachable", 1, 4, 4 }, { "packet-too-big", 2, 0, 0xFF }, { "time-exceeded", 3, 0, 0xFF }, /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, { "ttl-zero-during-transit", 3, 0, 0 }, { "ttl-zero-during-reassembly", 3, 1, 1 }, { "parameter-problem", 4, 0, 0xFF }, { "bad-header", 4, 0, 0 }, { "unknown-header-type", 4, 1, 1 }, { "unknown-option", 4, 2, 2 }, { "echo-request", 128, 0, 0xFF }, /* Alias */ { "ping", 128, 0, 0xFF }, { "echo-reply", 129, 0, 0xFF }, /* Alias */ { "pong", 129, 0, 0xFF }, { "router-solicitation", 133, 0, 0xFF }, { "router-advertisement", 134, 0, 0xFF }, { "neighbour-solicitation", 135, 0, 0xFF }, /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, { "neighbour-advertisement", 136, 0, 0xFF }, /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, { "redirect", 137, 0, 0xFF }, }; static void parse_port_range(const char *protocol, const char *portstring, uint16_t *ports) { char *buffer; char *cp; buffer = strdup(portstring); if ((cp = strchr(buffer, ':')) == NULL) ports[0] = ports[1] = xtables_parse_port(buffer, NULL); else { *cp = '\0'; cp++; ports[0] = buffer[0] ? xtables_parse_port(buffer, NULL) : 0; ports[1] = cp[0] ? xtables_parse_port(cp, NULL) : 0xFFFF; if (ports[0] > ports[1]) xtables_error(PARAMETER_PROBLEM, "invalid portrange (min > max)"); } free(buffer); } static char *parse_range(const char *str, unsigned int res[]) { char *next; if (!xtables_strtoui(str, &next, &res[0], 0, 255)) return NULL; res[1] = res[0]; if (*next == ':') { str = next + 1; if (!xtables_strtoui(str, &next, &res[1], 0, 255)) return NULL; } return next; } static int parse_icmpv6(const char *icmpv6type, uint8_t type[], uint8_t code[]) { static const unsigned int limit = ARRAY_SIZE(icmpv6_codes); unsigned int match = limit; unsigned int i, number[2]; for (i = 0; i < limit; i++) { if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type))) continue; if (match != limit) xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMPv6 type `%s':" " `%s' or `%s'?", icmpv6type, icmpv6_codes[match].name, icmpv6_codes[i].name); match = i; } if (match < limit) { type[0] = type[1] = icmpv6_codes[match].type; code[0] = icmpv6_codes[match].code_min; code[1] = icmpv6_codes[match].code_max; } else { char *next = parse_range(icmpv6type, number); if (!next) { xtables_error(PARAMETER_PROBLEM, "Unknown ICMPv6 type `%s'", icmpv6type); return -1; } type[0] = (uint8_t) number[0]; type[1] = (uint8_t) number[1]; switch (*next) { case 0: code[0] = 0; code[1] = 255; return 0; case '/': next = parse_range(next+1, number); code[0] = (uint8_t) number[0]; code[1] = (uint8_t) number[1]; if (next == NULL) return -1; if (next && *next == 0) return 0; /* fallthrough */ default: xtables_error(PARAMETER_PROBLEM, "unknown character %c", *next); return -1; } } return 0; } static void print_port_range(uint16_t *ports) { if (ports[0] == ports[1]) printf("%d ", ports[0]); else printf("%d:%d ", ports[0], ports[1]); } static void print_icmp_code(uint8_t *code) { if (code[0] == code[1]) printf("/%"PRIu8 " ", code[0]); else printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]); } static void print_icmp_type(uint8_t *type, uint8_t *code) { unsigned int i; if (type[0] != type[1]) { printf("%"PRIu8 ":%" PRIu8, type[0], type[1]); print_icmp_code(code); return; } for (i = 0; i < ARRAY_SIZE(icmpv6_codes); i++) { if (icmpv6_codes[i].type != type[0]) continue; if (icmpv6_codes[i].code_min == code[0] && icmpv6_codes[i].code_max == code[1]) { printf("%s ", icmpv6_codes[i].name); return; } } printf("%"PRIu8, type[0]); print_icmp_code(code); } static void brip6_print_help(void) { printf( "ip6 options:\n" "--ip6-src [!] address[/mask]: ipv6 source specification\n" "--ip6-dst [!] address[/mask]: ipv6 destination specification\n" "--ip6-tclass [!] tclass : ipv6 traffic class specification\n" "--ip6-proto [!] protocol : ipv6 protocol specification\n" "--ip6-sport [!] port[:port] : tcp/udp source port or port range\n" "--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n" "--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n"); printf("Valid ICMPv6 Types:"); xt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes)); } static void brip6_init(struct xt_entry_match *match) { struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; ipinfo->invflags = 0; ipinfo->bitmask = 0; memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr)); memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr)); memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr)); memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr)); } static struct in6_addr *numeric_to_addr(const char *num) { static struct in6_addr ap; int err; if ((err=inet_pton(AF_INET6, num, &ap)) == 1) return ≈ return (struct in6_addr *)NULL; } static struct in6_addr *parse_ip6_mask(char *mask) { static struct in6_addr maskaddr; struct in6_addr *addrp; unsigned int bits; if (mask == NULL) { /* no mask at all defaults to 128 bits */ memset(&maskaddr, 0xff, sizeof maskaddr); return &maskaddr; } if ((addrp = numeric_to_addr(mask)) != NULL) return addrp; if (!xtables_strtoui(mask, NULL, &bits, 0, 128)) xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Mask '%s' specified", mask); if (bits != 0) { char *p = (char *)&maskaddr; memset(p, 0xff, bits / 8); memset(p + (bits / 8) + 1, 0, (128 - bits) / 8); p[bits / 8] = 0xff << (8 - (bits & 7)); return &maskaddr; } memset(&maskaddr, 0, sizeof maskaddr); return &maskaddr; } /* Set the ipv6 mask and address. Callers should check ebt_errormsg[0]. * The string pointed to by address can be altered. */ static void ebt_parse_ip6_address(char *address, struct in6_addr *addr, struct in6_addr *msk) { struct in6_addr *tmp_addr; char buf[256]; char *p; int i; int err; strncpy(buf, address, sizeof(buf) - 1); /* first the mask */ buf[sizeof(buf) - 1] = '\0'; if ((p = strrchr(buf, '/')) != NULL) { *p = '\0'; tmp_addr = parse_ip6_mask(p + 1); } else tmp_addr = parse_ip6_mask(NULL); *msk = *tmp_addr; /* if a null mask is given, the name is ignored, like in "any/0" */ if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any))) strcpy(buf, "::"); if ((err=inet_pton(AF_INET6, buf, addr)) < 1) { xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 Address '%s' specified", buf); return; } for (i = 0; i < 4; i++) addr->s6_addr32[i] &= msk->s6_addr32[i]; } #define OPT_SOURCE 0x01 #define OPT_DEST 0x02 #define OPT_TCLASS 0x04 #define OPT_PROTO 0x08 #define OPT_SPORT 0x10 #define OPT_DPORT 0x20 static int brip6_parse(int c, char **argv, int invert, unsigned int *flags, const void *entry, struct xt_entry_match **match) { struct ebt_ip6_info *info = (struct ebt_ip6_info *)(*match)->data; unsigned int i; char *end; switch (c) { case IP_SOURCE: if (invert) info->invflags |= EBT_IP6_SOURCE; ebt_parse_ip6_address(optarg, &info->saddr, &info->smsk); info->bitmask |= EBT_IP6_SOURCE; break; case IP_DEST: if (invert) info->invflags |= EBT_IP6_DEST; ebt_parse_ip6_address(optarg, &info->daddr, &info->dmsk); info->bitmask |= EBT_IP6_DEST; break; case IP_SPORT: if (invert) info->invflags |= EBT_IP6_SPORT; parse_port_range(NULL, optarg, info->sport); info->bitmask |= EBT_IP6_SPORT; break; case IP_DPORT: if (invert) info->invflags |= EBT_IP6_DPORT; parse_port_range(NULL, optarg, info->dport); info->bitmask |= EBT_IP6_DPORT; break; case IP_ICMP6: if (invert) info->invflags |= EBT_IP6_ICMP6; if (parse_icmpv6(optarg, info->icmpv6_type, info->icmpv6_code)) return 0; info->bitmask |= EBT_IP6_ICMP6; break; case IP_TCLASS: if (invert) info->invflags |= EBT_IP6_TCLASS; if (!xtables_strtoui(optarg, &end, &i, 0, 255)) xtables_error(PARAMETER_PROBLEM, "Problem with specified IPv6 traffic class '%s'", optarg); info->tclass = i; info->bitmask |= EBT_IP6_TCLASS; break; case IP_PROTO: if (invert) info->invflags |= EBT_IP6_PROTO; info->protocol = xtables_parse_protocol(optarg); info->bitmask |= EBT_IP6_PROTO; break; default: return 0; } *flags |= info->bitmask; return 1; } static void brip6_final_check(unsigned int flags) { if (!flags) xtables_error(PARAMETER_PROBLEM, "You must specify proper arguments"); } static void brip6_print(const void *ip, const struct xt_entry_match *match, int numeric) { struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; if (ipinfo->bitmask & EBT_IP6_SOURCE) { printf("--ip6-src "); if (ipinfo->invflags & EBT_IP6_SOURCE) printf("! "); printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr)); printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk)); } if (ipinfo->bitmask & EBT_IP6_DEST) { printf("--ip6-dst "); if (ipinfo->invflags & EBT_IP6_DEST) printf("! "); printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr)); printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk)); } if (ipinfo->bitmask & EBT_IP6_TCLASS) { printf("--ip6-tclass "); if (ipinfo->invflags & EBT_IP6_TCLASS) printf("! "); printf("0x%02X ", ipinfo->tclass); } if (ipinfo->bitmask & EBT_IP6_PROTO) { struct protoent *pe; printf("--ip6-proto "); if (ipinfo->invflags & EBT_IP6_PROTO) printf("! "); pe = getprotobynumber(ipinfo->protocol); if (pe == NULL) { printf("%d ", ipinfo->protocol); } else { printf("%s ", pe->p_name); } } if (ipinfo->bitmask & EBT_IP6_SPORT) { printf("--ip6-sport "); if (ipinfo->invflags & EBT_IP6_SPORT) printf("! "); print_port_range(ipinfo->sport); } if (ipinfo->bitmask & EBT_IP6_DPORT) { printf("--ip6-dport "); if (ipinfo->invflags & EBT_IP6_DPORT) printf("! "); print_port_range(ipinfo->dport); } if (ipinfo->bitmask & EBT_IP6_ICMP6) { printf("--ip6-icmp-type "); if (ipinfo->invflags & EBT_IP6_ICMP6) printf("! "); print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code); } } static void brip_xlate_th(struct xt_xlate *xl, const struct ebt_ip6_info *info, int bit, const char *pname) { const uint16_t *ports; if ((info->bitmask & bit) == 0) return; switch (bit) { case EBT_IP6_SPORT: if (pname) xt_xlate_add(xl, "%s sport ", pname); else xt_xlate_add(xl, "@th,0,16 "); ports = info->sport; break; case EBT_IP6_DPORT: if (pname) xt_xlate_add(xl, "%s dport ", pname); else xt_xlate_add(xl, "@th,16,16 "); ports = info->dport; break; default: return; } if (info->invflags & bit) xt_xlate_add(xl, "!= "); if (ports[0] == ports[1]) xt_xlate_add(xl, "%d ", ports[0]); else xt_xlate_add(xl, "%d-%d ", ports[0], ports[1]); } static void brip_xlate_nh(struct xt_xlate *xl, const struct ebt_ip6_info *info, int bit) { struct in6_addr *addrp, *maskp; if ((info->bitmask & bit) == 0) return; switch (bit) { case EBT_IP6_SOURCE: xt_xlate_add(xl, "ip6 saddr "); addrp = (struct in6_addr *)&info->saddr; maskp = (struct in6_addr *)&info->smsk; break; case EBT_IP6_DEST: xt_xlate_add(xl, "ip6 daddr "); addrp = (struct in6_addr *)&info->daddr; maskp = (struct in6_addr *)&info->dmsk; break; default: return; } if (info->invflags & bit) xt_xlate_add(xl, "!= "); xt_xlate_add(xl, "%s%s ", xtables_ip6addr_to_numeric(addrp), xtables_ip6mask_to_numeric(maskp)); } static const char *brip6_xlate_proto_to_name(uint8_t proto) { switch (proto) { case IPPROTO_TCP: return "tcp"; case IPPROTO_UDP: return "udp"; case IPPROTO_UDPLITE: return "udplite"; case IPPROTO_SCTP: return "sctp"; case IPPROTO_DCCP: return "dccp"; default: return NULL; } } static int brip6_xlate(struct xt_xlate *xl, const struct xt_xlate_mt_params *params) { const struct ebt_ip6_info *info = (const void *)params->match->data; const char *pname = NULL; if ((info->bitmask & (EBT_IP6_SOURCE|EBT_IP6_DEST|EBT_IP6_ICMP6|EBT_IP6_TCLASS)) == 0) xt_xlate_add(xl, "ether type ip6 "); brip_xlate_nh(xl, info, EBT_IP6_SOURCE); brip_xlate_nh(xl, info, EBT_IP6_DEST); if (info->bitmask & EBT_IP6_TCLASS) { xt_xlate_add(xl, "ip6 dscp "); if (info->invflags & EBT_IP6_TCLASS) xt_xlate_add(xl, "!= "); xt_xlate_add(xl, "0x%02x ", info->tclass & 0x3f); /* remove ECN bits */ } if (info->bitmask & EBT_IP6_PROTO) { struct protoent *pe; if (info->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT|EBT_IP6_ICMP6) && (info->invflags & EBT_IP6_PROTO) == 0) { /* port number given and not inverted, no need to * add explicit 'meta l4proto'. */ pname = brip6_xlate_proto_to_name(info->protocol); } else { xt_xlate_add(xl, "meta l4proto "); if (info->invflags & EBT_IP6_PROTO) xt_xlate_add(xl, "!= "); pe = getprotobynumber(info->protocol); if (pe == NULL) xt_xlate_add(xl, "%d ", info->protocol); else xt_xlate_add(xl, "%s ", pe->p_name); } } brip_xlate_th(xl, info, EBT_IP6_SPORT, pname); brip_xlate_th(xl, info, EBT_IP6_DPORT, pname); if (info->bitmask & EBT_IP6_ICMP6) { xt_xlate_add(xl, "icmpv6 type "); if (info->invflags & EBT_IP6_ICMP6) xt_xlate_add(xl, "!= "); if (info->icmpv6_type[0] == info->icmpv6_type[1]) xt_xlate_add(xl, "%d ", info->icmpv6_type[0]); else xt_xlate_add(xl, "%d-%d ", info->icmpv6_type[0], info->icmpv6_type[1]); if (info->icmpv6_code[0] == 0 && info->icmpv6_code[1] == 0xff) return 1; xt_xlate_add(xl, "icmpv6 code "); if (info->invflags & EBT_IP6_ICMP6) xt_xlate_add(xl, "!= "); if (info->icmpv6_code[0] == info->icmpv6_code[1]) xt_xlate_add(xl, "%d ", info->icmpv6_code[0]); else xt_xlate_add(xl, "%d-%d ", info->icmpv6_code[0], info->icmpv6_code[1]); } return 1; } static struct xtables_match brip6_match = { .name = "ip6", .revision = 0, .version = XTABLES_VERSION, .family = NFPROTO_BRIDGE, .size = XT_ALIGN(sizeof(struct ebt_ip6_info)), .userspacesize = XT_ALIGN(sizeof(struct ebt_ip6_info)), .init = brip6_init, .help = brip6_print_help, .parse = brip6_parse, .final_check = brip6_final_check, .print = brip6_print, .xlate = brip6_xlate, .extra_opts = brip6_opts, }; void _init(void) { xtables_register_match(&brip6_match); }