]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
libebt_ip: add icmp support
authorFlorian Westphal <fw@strlen.de>
Tue, 17 Apr 2018 22:09:06 +0000 (00:09 +0200)
committerFlorian Westphal <fw@strlen.de>
Thu, 19 Apr 2018 08:18:22 +0000 (10:18 +0200)
Was added to ebtables recently, so backport this to ebt-compat.

Signed-off-by: Florian Westphal <fw@strlen.de>
extensions/libebt_ip.c
extensions/libebt_ip.txlate

index 1a87585c533fc73901406ee58989a4f2055be0b8..72728542f444db811a95cbc623e3c91d277a5418 100644 (file)
 #include <string.h>
 #include <getopt.h>
 #include <netdb.h>
+#include <inttypes.h>
 #include <xtables.h>
-#include <linux/netfilter_bridge/ebt_ip.h>
+
+#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;
 }
 
index 4d31a700814ca88c1a2c4cce34f11391025d32ae..11594cdc0ad8b76e2c105ce694a32f77a65b168c 100644 (file)
@@ -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