From b6f0bec11e2cc7c3f99d432a5e1204a855f361dd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Apr 2018 00:09:06 +0200 Subject: libebt_ip: add icmp support Was added to ebtables recently, so backport this to ebt-compat. Signed-off-by: Florian Westphal --- extensions/libebt_ip.c | 297 +++++++++++++++++++++++++++++++++++++++++++- extensions/libebt_ip.txlate | 9 ++ 2 files changed, 304 insertions(+), 2 deletions(-) diff --git a/extensions/libebt_ip.c b/extensions/libebt_ip.c index 1a87585c..72728542 100644 --- a/extensions/libebt_ip.c +++ b/extensions/libebt_ip.c @@ -18,8 +18,41 @@ #include #include #include +#include #include -#include + +#include "libxt_icmp.h" + +#define EBT_IP_SOURCE 0x01 +#define EBT_IP_DEST 0x02 +#define EBT_IP_TOS 0x04 +#define EBT_IP_PROTO 0x08 +#define EBT_IP_SPORT 0x10 +#define EBT_IP_DPORT 0x20 +#define EBT_IP_ICMP 0x40 +#define EBT_IP_IGMP 0x80 +#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ + EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP) + +struct ebt_ip_info { + __be32 saddr; + __be32 daddr; + __be32 smsk; + __be32 dmsk; + __u8 tos; + __u8 protocol; + __u8 bitmask; + __u8 invflags; + union { + __u16 sport[2]; + __u8 icmp_type[2]; + __u8 igmp_type[2]; + }; + union { + __u16 dport[2]; + __u8 icmp_code[2]; + }; +}; #define IP_SOURCE '1' #define IP_DEST '2' @@ -27,6 +60,8 @@ #define IP_PROTO '4' #define IP_SPORT '5' #define IP_DPORT '6' +#define IP_EBT_ICMP '7' +#define IP_EBT_IGMP '8' static const struct option brip_opts[] = { { .name = "ip-source", .has_arg = true, .val = IP_SOURCE }, @@ -40,9 +75,73 @@ static const struct option brip_opts[] = { { .name = "ip-sport", .has_arg = true, .val = IP_SPORT }, { .name = "ip-destination-port",.has_arg = true, .val = IP_DPORT }, { .name = "ip-dport", .has_arg = true, .val = IP_DPORT }, + { .name = "ip-icmp-type", .has_arg = true, .val = IP_EBT_ICMP }, + { .name = "ip-igmp-type", .has_arg = true, .val = IP_EBT_IGMP }, XT_GETOPT_TABLEEND, }; +static const struct xt_icmp_names icmp_codes[] = { + { "echo-reply", 0, 0, 0xFF }, + /* Alias */ { "pong", 0, 0, 0xFF }, + + { "destination-unreachable", 3, 0, 0xFF }, + { "network-unreachable", 3, 0, 0 }, + { "host-unreachable", 3, 1, 1 }, + { "protocol-unreachable", 3, 2, 2 }, + { "port-unreachable", 3, 3, 3 }, + { "fragmentation-needed", 3, 4, 4 }, + { "source-route-failed", 3, 5, 5 }, + { "network-unknown", 3, 6, 6 }, + { "host-unknown", 3, 7, 7 }, + { "network-prohibited", 3, 9, 9 }, + { "host-prohibited", 3, 10, 10 }, + { "TOS-network-unreachable", 3, 11, 11 }, + { "TOS-host-unreachable", 3, 12, 12 }, + { "communication-prohibited", 3, 13, 13 }, + { "host-precedence-violation", 3, 14, 14 }, + { "precedence-cutoff", 3, 15, 15 }, + + { "source-quench", 4, 0, 0xFF }, + + { "redirect", 5, 0, 0xFF }, + { "network-redirect", 5, 0, 0 }, + { "host-redirect", 5, 1, 1 }, + { "TOS-network-redirect", 5, 2, 2 }, + { "TOS-host-redirect", 5, 3, 3 }, + + { "echo-request", 8, 0, 0xFF }, + /* Alias */ { "ping", 8, 0, 0xFF }, + + { "router-advertisement", 9, 0, 0xFF }, + + { "router-solicitation", 10, 0, 0xFF }, + + { "time-exceeded", 11, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, + { "ttl-zero-during-transit", 11, 0, 0 }, + { "ttl-zero-during-reassembly", 11, 1, 1 }, + + { "parameter-problem", 12, 0, 0xFF }, + { "ip-header-bad", 12, 0, 0 }, + { "required-option-missing", 12, 1, 1 }, + + { "timestamp-request", 13, 0, 0xFF }, + + { "timestamp-reply", 14, 0, 0xFF }, + + { "address-mask-request", 17, 0, 0xFF }, + + { "address-mask-reply", 18, 0, 0xFF } +}; + +static const struct xt_icmp_names igmp_types[] = { + { "membership-query", 0x11 }, + { "membership-report-v1", 0x12 }, + { "membership-report-v2", 0x16 }, + { "leave-group", 0x17 }, + { "membership-report-v3", 0x22 }, +}; + static void brip_print_help(void) { printf( @@ -52,7 +151,14 @@ static void brip_print_help(void) "--ip-tos [!] tos : ip tos specification\n" "--ip-proto [!] protocol : ip protocol specification\n" "--ip-sport [!] port[:port] : tcp/udp source port or port range\n" -"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n"); +"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n" +"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n" +"--ip-igmp-type [!] type[:type] : igmp type or type range\n"); + + printf("\nValid ICMP Types:\n"); + xt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes)); + printf("\nValid IGMP Types:\n"); + xt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types)); } static void brip_init(struct xt_entry_match *match) @@ -161,6 +267,118 @@ static void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk) *addr = *addr & *msk; } +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 ebt_parse_icmp(const struct xt_icmp_names *codes, size_t n_codes, + const char *icmptype, uint8_t type[], uint8_t code[]) +{ + unsigned int match = n_codes; + unsigned int i, number[2]; + + for (i = 0; i < n_codes; i++) { + if (strncasecmp(codes[i].name, icmptype, strlen(icmptype))) + continue; + if (match != n_codes) + xtables_error(PARAMETER_PROBLEM, "Ambiguous ICMP type `%s':" + " `%s' or `%s'?", + icmptype, codes[match].name, + codes[i].name); + match = i; + } + + if (match < n_codes) { + type[0] = type[1] = codes[match].type; + if (code) { + code[0] = codes[match].code_min; + code[1] = codes[match].code_max; + } + } else { + char *next = parse_range(icmptype, number); + if (!next) { + xtables_error(PARAMETER_PROBLEM, "Unknown ICMP type `%s'", + icmptype); + return -1; + } + + type[0] = (uint8_t) number[0]; + type[1] = (uint8_t) number[1]; + switch (*next) { + case 0: + if (code) { + code[0] = 0; + code[1] = 255; + } + return 0; + case '/': + if (code) { + 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_icmp_code(uint8_t *code) +{ + if (!code) + return; + + if (code[0] == code[1]) + printf("/%"PRIu8 " ", code[0]); + else + printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]); +} + +static void ebt_print_icmp_type(const struct xt_icmp_names *codes, + size_t n_codes, 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 < n_codes; i++) { + if (codes[i].type != type[0]) + continue; + + if (!code || (codes[i].code_min == code[0] && + codes[i].code_max == code[1])) { + printf("%s ", codes[i].name); + return; + } + } + printf("%"PRIu8, type[0]); + print_icmp_code(code); +} + static int brip_parse(int c, char **argv, int invert, unsigned int *flags, const void *entry, struct xt_entry_match **match) @@ -192,6 +410,20 @@ brip_parse(int c, char **argv, int invert, unsigned int *flags, parse_port_range(NULL, optarg, info->dport); info->bitmask |= EBT_IP_DPORT; break; + case IP_EBT_ICMP: + if (invert) + info->invflags |= EBT_IP_ICMP; + ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg, + info->icmp_type, info->icmp_code); + info->bitmask |= EBT_IP_ICMP; + break; + case IP_EBT_IGMP: + if (invert) + info->invflags |= EBT_IP_IGMP; + ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg, + info->igmp_type, NULL); + info->bitmask |= EBT_IP_IGMP; + break; case IP_EBT_TOS: if (invert) info->invflags |= EBT_IP_TOS; @@ -289,6 +521,20 @@ static void brip_print(const void *ip, const struct xt_entry_match *match, printf("! "); print_port_range(info->dport); } + if (info->bitmask & EBT_IP_ICMP) { + printf("--ip-icmp-type "); + if (info->invflags & EBT_IP_ICMP) + printf("! "); + ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes), + info->icmp_type, info->icmp_code); + } + if (info->bitmask & EBT_IP_IGMP) { + printf("--ip-igmp-type "); + if (info->invflags & EBT_IP_IGMP) + printf("! "); + ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types), + info->igmp_type, NULL); + } } static const char *brip_xlate_proto_to_name(uint8_t proto) @@ -309,6 +555,50 @@ static const char *brip_xlate_proto_to_name(uint8_t proto) } } +static void brip_xlate_icmp(struct xt_xlate *xl, + const struct ebt_ip_info *info, int bit) +{ + if ((info->bitmask & bit) == 0) + return; + + xt_xlate_add(xl, "ip icmp type "); + if (info->invflags & bit) + xt_xlate_add(xl, "!= "); + if (info->icmp_type[0] == info->icmp_type[1]) + xt_xlate_add(xl, "%d ", info->icmp_type[0]); + else + xt_xlate_add(xl, "%d-%d ", info->icmp_type[0], + info->icmp_type[1]); + if (info->icmp_code[0] == 0 && + info->icmp_code[1] == 0xff) + return; + + xt_xlate_add(xl, "ip icmp code "); + if (info->invflags & bit) + xt_xlate_add(xl, "!= "); + if (info->icmp_code[0] == info->icmp_code[1]) + xt_xlate_add(xl, "%d ", info->icmp_code[0]); + else + xt_xlate_add(xl, "%d-%d ", info->icmp_code[0], + info->icmp_code[1]); +} + +static void brip_xlate_igmp(struct xt_xlate *xl, + const struct ebt_ip_info *info, int bit) +{ + if ((info->bitmask & bit) == 0) + return; + + xt_xlate_add(xl, "@th,0,8 "); + if (info->invflags & bit) + xt_xlate_add(xl, "!= "); + if (info->icmp_type[0] == info->icmp_type[1]) + xt_xlate_add(xl, "%d ", info->icmp_type[0]); + else + xt_xlate_add(xl, "%d-%d ", info->icmp_type[0], + info->icmp_type[1]); +} + static void brip_xlate_th(struct xt_xlate *xl, const struct ebt_ip_info *info, int bit, const char *pname) @@ -415,6 +705,9 @@ static int brip_xlate(struct xt_xlate *xl, brip_xlate_th(xl, info, EBT_IP_SPORT, pname); brip_xlate_th(xl, info, EBT_IP_DPORT, pname); + brip_xlate_icmp(xl, info, EBT_IP_ICMP); + brip_xlate_igmp(xl, info, EBT_IP_IGMP); + return 1; } diff --git a/extensions/libebt_ip.txlate b/extensions/libebt_ip.txlate index 4d31a700..11594cdc 100644 --- a/extensions/libebt_ip.txlate +++ b/extensions/libebt_ip.txlate @@ -15,3 +15,12 @@ nft add rule bridge filter FORWARD udp sport 1024-65535 counter ebtables-translate -A FORWARD --ip-proto 253 nft add rule bridge filter FORWARD ip protocol 253 counter + +ebtables-translate -A FORWARD --ip-icmp-type "echo-request" +nft add rule bridge filter FORWARD ip icmp type 8 counter + +ebtables-translate -A FORWARD --ip-icmp-type 1/1 +nft add rule bridge filter FORWARD ip icmp type 1 ip icmp code 1 counter + +ebtables-translate -A FORWARD --ip-icmp-type ! 1:10 +nft add rule bridge filter FORWARD ip icmp type != 1-10 counter -- cgit v1.2.3