]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
nft: bootstrap ebtables-compat
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 9 Oct 2014 13:02:02 +0000 (15:02 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 24 Nov 2014 10:35:33 +0000 (11:35 +0100)
This patch bootstraps ebtables-compat, the ebtables compatibility
software upon nf_tables.

[ Original patches:

  http://patchwork.ozlabs.org/patch/395544/
  http://patchwork.ozlabs.org/patch/395545/
  http://patchwork.ozlabs.org/patch/395546/

I have also forward port them on top of the current git HEAD, otherwise
compilation breaks.

This bootstrap is experimental, this still needs more work. --Pablo ]

Signed-off-by: Giuseppe Longo <giuseppelng@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
16 files changed:
include/ebtables/ethernetdb.h [new file with mode: 0644]
include/linux/netfilter_bridge.h [new file with mode: 0644]
include/linux/netfilter_bridge/ebtables.h [new file with mode: 0644]
iptables/Makefile.am
iptables/getethertype.c [new file with mode: 0644]
iptables/nft-bridge.c [new file with mode: 0644]
iptables/nft-shared.c
iptables/nft-shared.h
iptables/nft.c
iptables/nft.h
iptables/xtables-compat-multi.c
iptables/xtables-eb-standalone.c [new file with mode: 0644]
iptables/xtables-eb.c [new file with mode: 0644]
iptables/xtables-ebtables.h [new file with mode: 0644]
iptables/xtables-multi.c
iptables/xtables-multi.h

diff --git a/include/ebtables/ethernetdb.h b/include/ebtables/ethernetdb.h
new file mode 100644 (file)
index 0000000..46d8bfd
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* All data returned by the network data base library are supplied in
+   host order and returned in network order (suitable for use in
+   system calls).  */
+
+#ifndef        _ETHERNETDB_H
+#define        _ETHERNETDB_H   1
+
+#include <features.h>
+#include <netinet/in.h>
+#include <stdint.h>
+
+/* Absolute file name for network data base files.  */
+#ifndef        _PATH_ETHERTYPES
+#define        _PATH_ETHERTYPES        "/etc/ethertypes"
+#endif                         /* _PATH_ETHERTYPES */
+
+struct ethertypeent {
+       char *e_name;           /* Official ethernet type name.  */
+       char **e_aliases;       /* Alias list.  */
+       int e_ethertype;        /* Ethernet type number.  */
+};
+
+/* Open ethertype data base files and mark them as staying open even
+   after a later search if STAY_OPEN is non-zero.  */
+extern void setethertypeent(int __stay_open) __THROW;
+
+/* Close ethertype data base files and clear `stay open' flag.  */
+extern void endethertypeent(void) __THROW;
+
+/* Get next entry from ethertype data base file.  Open data base if
+   necessary.  */
+extern struct ethertypeent *getethertypeent(void) __THROW;
+
+/* Return entry from ethertype data base for network with NAME.  */
+extern struct ethertypeent *getethertypebyname(__const char *__name)
+    __THROW;
+
+/* Return entry from ethertype data base which number is PROTO.  */
+extern struct ethertypeent *getethertypebynumber(int __ethertype) __THROW;
+
+
+#endif                         /* ethernetdb.h */
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
new file mode 100644 (file)
index 0000000..71e9299
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter. 
+ */
+#include <limits.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING      0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN         1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD          2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT                3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING     4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING         5
+#define NF_BR_NUMHOOKS         6
+
+enum nf_br_hook_priorities {
+       NF_BR_PRI_FIRST = INT_MIN,
+       NF_BR_PRI_FILTER_BRIDGED = -200,
+       NF_BR_PRI_FILTER_OTHER = 200,
+       NF_BR_PRI_NAT_DST_BRIDGED = -300,
+       NF_BR_PRI_NAT_DST_OTHER = 100,
+       NF_BR_PRI_NAT_SRC = 300,
+       NF_BR_PRI_LAST = INT_MAX,
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
new file mode 100644 (file)
index 0000000..f7ed1dc
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ *  ebtables
+ *
+ *     Authors:
+ *     Bart De Schuymer                <bdschuym@pandora.be>
+ *
+ *  ebtables.c,v 2.0, April, 2002
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+/* Local copy of the kernel file, needed for Sparc64 support */
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <net/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+/* verdicts >0 are "branches" */
+#define EBT_ACCEPT   -1
+#define EBT_DROP     -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN   -4
+#define NUM_STANDARD_TARGETS   4
+/* ebtables target modules store the verdict inside an int. We can
+ * reclaim a part of this int for backwards compatible extensions.
+ * The 4 lsb are more than enough to store the verdict. */
+#define EBT_VERDICT_BITS 0x0000000F
+
+struct ebt_counter
+{
+       uint64_t pcnt;
+       uint64_t bcnt;
+};
+
+struct ebt_replace
+{
+       char name[EBT_TABLE_MAXNAMELEN];
+       unsigned int valid_hooks;
+       /* nr of rules in the table */
+       unsigned int nentries;
+       /* total size of the entries */
+       unsigned int entries_size;
+       /* start of the chains */
+#ifdef KERNEL_64_USERSPACE_32
+       uint64_t hook_entry[NF_BR_NUMHOOKS];
+#else
+       struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+#endif
+       /* nr of counters userspace expects back */
+       unsigned int num_counters;
+       /* where the kernel will put the old counters */
+#ifdef KERNEL_64_USERSPACE_32
+       uint64_t counters;
+       uint64_t entries;
+#else
+       struct ebt_counter *counters;
+       char *entries;
+#endif
+};
+
+struct ebt_entries {
+       /* this field is always set to zero
+        * See EBT_ENTRY_OR_ENTRIES.
+        * Must be same size as ebt_entry.bitmask */
+       unsigned int distinguisher;
+       /* the chain name */
+       char name[EBT_CHAIN_MAXNAMELEN];
+       /* counter offset for this chain */
+       unsigned int counter_offset;
+       /* one standard (accept, drop, return) per hook */
+       int policy;
+       /* nr. of entries */
+       unsigned int nentries;
+       /* entry list */
+       char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* used for the bitmask of struct ebt_entry */
+
+/* This is a hack to make a difference between an ebt_entry struct and an
+ * ebt_entries struct when traversing the entries from start to end.
+ * Using this simplifies the code alot, while still being able to use
+ * ebt_entries.
+ * Contrary, iptables doesn't use something like ebt_entries and therefore uses
+ * different techniques for naming the policy and such. So, iptables doesn't
+ * need a hack like this.
+ */
+#define EBT_ENTRY_OR_ENTRIES 0x01
+/* these are the normal masks */
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+   | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match
+{
+       union {
+               char name[EBT_FUNCTION_MAXNAMELEN];
+               struct ebt_match *match;
+       } u;
+       /* size of data */
+       unsigned int match_size;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_watcher
+{
+       union {
+               char name[EBT_FUNCTION_MAXNAMELEN];
+               struct ebt_watcher *watcher;
+       } u;
+       /* size of data */
+       unsigned int watcher_size;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_target
+{
+       union {
+               char name[EBT_FUNCTION_MAXNAMELEN];
+               struct ebt_target *target;
+       } u;
+       /* size of data */
+       unsigned int target_size;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+       struct ebt_entry_target target;
+       int verdict;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+};
+
+/* one entry */
+struct ebt_entry {
+       /* this needs to be the first field */
+       unsigned int bitmask;
+       unsigned int invflags;
+       uint16_t ethproto;
+       /* the physical in-dev */
+       char in[IFNAMSIZ];
+       /* the logical in-dev */
+       char logical_in[IFNAMSIZ];
+       /* the physical out-dev */
+       char out[IFNAMSIZ];
+       /* the logical out-dev */
+       char logical_out[IFNAMSIZ];
+       unsigned char sourcemac[ETH_ALEN];
+       unsigned char sourcemsk[ETH_ALEN];
+       unsigned char destmac[ETH_ALEN];
+       unsigned char destmsk[ETH_ALEN];
+       /* sizeof ebt_entry + matches */
+       unsigned int watchers_offset;
+       /* sizeof ebt_entry + matches + watchers */
+       unsigned int target_offset;
+       /* sizeof ebt_entry + matches + watchers + target */
+       unsigned int next_offset;
+       unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* {g,s}etsockopt numbers */
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+/* blatently stolen from ip_tables.h
+ * fn returns 0 to continue iteration */
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry_match *__match;                    \
+                                                           \
+       for (__i = sizeof(struct ebt_entry);                \
+            __i < (e)->watchers_offset;                    \
+            __i += __match->match_size +                   \
+            sizeof(struct ebt_entry_match)) {              \
+               __match = (void *)(e) + __i;                \
+                                                           \
+               __ret = fn(__match , ## args);              \
+               if (__ret != 0)                             \
+                       break;                              \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (e)->watchers_offset)            \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry_watcher *__watcher;                \
+                                                           \
+       for (__i = e->watchers_offset;                      \
+            __i < (e)->target_offset;                      \
+            __i += __watcher->watcher_size +               \
+            sizeof(struct ebt_entry_watcher)) {            \
+               __watcher = (void *)(e) + __i;              \
+                                                           \
+               __ret = fn(__watcher , ## args);            \
+               if (__ret != 0)                             \
+                       break;                              \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (e)->target_offset)              \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry *__entry;                          \
+                                                           \
+       for (__i = 0; __i < (size);) {                      \
+               __entry = (void *)(entries) + __i;          \
+               __ret = fn(__entry , ## args);              \
+               if (__ret != 0)                             \
+                       break;                              \
+               if (__entry->bitmask != 0)                  \
+                       __i += __entry->next_offset;        \
+               else                                        \
+                       __i += sizeof(struct ebt_entries);  \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (size))                          \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#endif
index aaaade85550315be016010a4a4f09f1ed75f40fb..b3e417bf4332cda625113c464c2e4ead71b6a59e 100644 (file)
@@ -39,7 +39,9 @@ xtables_compat_multi_SOURCES += xtables-save.c xtables-restore.c \
                                xtables-standalone.c xtables.c nft.c \
                                nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \
                                xtables-config.c xtables-events.c \
-                               xtables-arp-standalone.c xtables-arp.c
+                               xtables-arp-standalone.c xtables-arp.c \
+                               getethertype.c nft-bridge.c \
+                               xtables-eb-standalone.c xtables-eb.c
 xtables_compat_multi_LDADD   += ${libmnl_LIBS} ${libnftnl_LIBS} ../extensions/libext4.a ../extensions/libext6.a
 # yacc and lex generate dirty code
 xtables_compat_multi-xtables-config-parser.o xtables_compat_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
@@ -67,7 +69,7 @@ endif
 if ENABLE_NFTABLES
 x_sbin_links  = iptables-compat iptables-compat-restore iptables-compat-save \
                ip6tables-compat ip6tables-compat-restore ip6tables-compat-save \
-               arptables-compat xtables-config xtables-events
+               arptables-compat ebtables-compat xtables-config xtables-events
 endif
 
 iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
diff --git a/iptables/getethertype.c b/iptables/getethertype.c
new file mode 100644 (file)
index 0000000..027ef4a
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+* getethertype.c
+*
+* This file was part of the NYS Library.
+*
+** The NYS Library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library General Public License as
+** published by the Free Software Foundation; either version 2 of the
+** License, or (at your option) any later version.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/********************************************************************
+* Description: Ethertype name service switch and the ethertypes 
+* database access functions
+* Author: Nick Fedchik <fnm@ukrsat.com>
+* Checker: Bart De Schuymer <bdschuym@pandora.be>
+* Origin: uClibc-0.9.16/libc/inet/getproto.c
+* Created at: Mon Nov 11 12:20:11 EET 2002
+********************************************************************/
+
+#include <ctype.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+
+#include <ebtables/ethernetdb.h>
+
+#define        MAXALIASES      35
+
+static FILE *etherf = NULL;
+static char line[BUFSIZ + 1];
+static struct ethertypeent et_ent;
+static char *ethertype_aliases[MAXALIASES];
+static int ethertype_stayopen;
+
+void setethertypeent(int f)
+{
+       if (etherf == NULL)
+               etherf = fopen(_PATH_ETHERTYPES, "r");
+       else
+               rewind(etherf);
+       ethertype_stayopen |= f;
+}
+
+void endethertypeent(void)
+{
+       if (etherf) {
+               fclose(etherf);
+               etherf = NULL;
+       }
+       ethertype_stayopen = 0;
+}
+
+struct ethertypeent *getethertypeent(void)
+{
+       char *e;
+       char *endptr;
+       register char *cp, **q;
+
+       if (etherf == NULL
+           && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
+               return (NULL);
+       }
+
+again:
+       if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
+               return (NULL);
+       }
+       if (*e == '#')
+               goto again;
+       cp = strpbrk(e, "#\n");
+       if (cp == NULL)
+               goto again;
+       *cp = '\0';
+       et_ent.e_name = e;
+       cp = strpbrk(e, " \t");
+       if (cp == NULL)
+               goto again;
+       *cp++ = '\0';
+       while (*cp == ' ' || *cp == '\t')
+               cp++;
+       e = strpbrk(cp, " \t");
+       if (e != NULL)
+               *e++ = '\0';
+// Check point
+       et_ent.e_ethertype = strtol(cp, &endptr, 16);
+       if (*endptr != '\0'
+           || (et_ent.e_ethertype < ETH_ZLEN
+               || et_ent.e_ethertype > 0xFFFF))
+               goto again;     // Skip invalid etherproto type entry
+       q = et_ent.e_aliases = ethertype_aliases;
+       if (e != NULL) {
+               cp = e;
+               while (cp && *cp) {
+                       if (*cp == ' ' || *cp == '\t') {
+                               cp++;
+                               continue;
+                       }
+                       if (q < &ethertype_aliases[MAXALIASES - 1])
+                               *q++ = cp;
+                       cp = strpbrk(cp, " \t");
+                       if (cp != NULL)
+                               *cp++ = '\0';
+               }
+       }
+       *q = NULL;
+       return (&et_ent);
+}
+
+
+struct ethertypeent *getethertypebyname(const char *name)
+{
+       register struct ethertypeent *e;
+       register char **cp;
+
+       setethertypeent(ethertype_stayopen);
+       while ((e = getethertypeent()) != NULL) {
+               if (strcasecmp(e->e_name, name) == 0)
+                       break;
+               for (cp = e->e_aliases; *cp != 0; cp++)
+                       if (strcasecmp(*cp, name) == 0)
+                               goto found;
+       }
+found:
+       if (!ethertype_stayopen)
+               endethertypeent();
+       return (e);
+}
+
+struct ethertypeent *getethertypebynumber(int type)
+{
+       register struct ethertypeent *e;
+
+       setethertypeent(ethertype_stayopen);
+       while ((e = getethertypeent()) != NULL)
+               if (e->e_ethertype == type)
+                       break;
+       if (!ethertype_stayopen)
+               endethertypeent();
+       return (e);
+}
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
new file mode 100644 (file)
index 0000000..66361ee
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * (C) 2014 by Giuseppe Longo <giuseppelng@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+
+#include <xtables.h>
+#include <libiptc/libxtc.h>
+#include <linux/netfilter/nf_tables.h>
+#include <ebtables/ethernetdb.h>
+
+#include "nft-shared.h"
+#include "nft.h"
+
+/* 0: default, print only 2 digits if necessary
+ * 2: always print 2 digits, a printed mac address
+ * then always has the same length */
+int ebt_printstyle_mac;
+
+static void ebt_print_mac(const unsigned char *mac)
+{
+       if (ebt_printstyle_mac == 2) {
+               int j;
+               for (j = 0; j < ETH_ALEN; j++)
+                       printf("%02x%s", mac[j],
+                               (j==ETH_ALEN-1) ? "" : ":");
+       } else
+               printf("%s", ether_ntoa((struct ether_addr *) mac));
+}
+
+/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
+static void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
+{
+       char hlpmsk[6] = {};
+
+       if (!memcmp(mac, eb_mac_type_unicast, 6) &&
+           !memcmp(mask, eb_msk_type_unicast, 6))
+               printf("Unicast");
+       else if (!memcmp(mac, eb_mac_type_multicast, 6) &&
+                !memcmp(mask, eb_msk_type_multicast, 6))
+               printf("Multicast");
+       else if (!memcmp(mac, eb_mac_type_broadcast, 6) &&
+                !memcmp(mask, eb_msk_type_broadcast, 6))
+               printf("Broadcast");
+       else if (!memcmp(mac, eb_mac_type_bridge_group, 6) &&
+                !memcmp(mask, eb_msk_type_bridge_group, 6))
+               printf("BGA");
+       else {
+               ebt_print_mac(mac);
+               if (memcmp(mask, hlpmsk, 6)) {
+                       printf("/");
+                       ebt_print_mac(mask);
+               }
+       }
+}
+
+static uint8_t ebt_to_ipt_flags(uint16_t invflags)
+{
+       uint8_t result = 0;
+
+       if (invflags & EBT_IIN)
+               result |= IPT_INV_VIA_IN;
+
+       if (invflags & EBT_IOUT)
+               result |= IPT_INV_VIA_OUT;
+
+       if (invflags & EBT_IPROTO)
+               result |= IPT_INV_PROTO;
+
+       return result;
+}
+
+static uint16_t ipt_to_ebt_flags(uint8_t invflags)
+{
+       uint16_t result = 0;
+
+       if (invflags & IPT_INV_VIA_IN)
+               result |= EBT_IIN;
+
+       if (invflags & IPT_INV_VIA_OUT)
+               result |= EBT_IOUT;
+
+       if (invflags & IPT_INV_PROTO)
+               result |= EBT_IPROTO;
+
+       return result;
+}
+
+static void add_logical_iniface(struct nft_rule *r, char *iface, int invflags)
+{
+       int iface_len;
+       uint32_t op;
+
+       iface_len = strlen(iface);
+
+       if (invflags & EBT_ILOGICALIN)
+               op = NFT_CMP_NEQ;
+       else
+               op = NFT_CMP_EQ;
+
+       add_meta(r, NFT_META_BRI_IIFNAME);
+       if (iface[iface_len - 1] == '+')
+               add_cmp_ptr(r, op, iface, iface_len - 1);
+       else
+               add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+static void add_logical_outiface(struct nft_rule *r, char *iface, int invflags)
+{
+       int iface_len;
+       uint32_t op;
+
+       iface_len = strlen(iface);
+
+       if (invflags & EBT_ILOGICALOUT)
+               op = NFT_CMP_NEQ;
+       else
+               op = NFT_CMP_EQ;
+
+       add_meta(r, NFT_META_BRI_OIFNAME);
+       if (iface[iface_len - 1] == '+')
+               add_cmp_ptr(r, op, iface, iface_len - 1);
+       else
+               add_cmp_ptr(r, op, iface, iface_len + 1);
+}
+
+static int _add_action(struct nft_rule *r, struct xtables_ebt_entry *fw)
+{
+       int ret = 0;
+
+       /* If no target at all, add nothing (default to continue) */
+       if (fw->target != NULL) {
+              /* Standard target? */
+              if (strcmp(fw->jumpto, XTC_LABEL_ACCEPT) == 0)
+                      ret = add_verdict(r, NF_ACCEPT);
+              else if (strcmp(fw->jumpto, XTC_LABEL_DROP) == 0)
+                      ret = add_verdict(r, NF_DROP);
+              else if (strcmp(fw->jumpto, XTC_LABEL_RETURN) == 0)
+                      ret = add_verdict(r, NFT_RETURN);
+              else
+                      ret = add_target(r, fw->target->t);
+       } else if (strlen(fw->jumpto) > 0)
+               /* Not standard, then it's a jump to chain */
+               ret = add_jumpto(r, fw->jumpto, NFT_JUMP);
+
+       return ret;
+}
+
+static int nft_bridge_add(struct nft_rule *r, void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+       uint8_t flags = ebt_to_ipt_flags(fw->invflags);
+       char *addr;
+
+       if (fw->in[0] != '\0')
+               add_iniface(r, fw->in, flags);
+
+       if (fw->out[0] != '\0')
+               add_outiface(r, fw->out, flags);
+
+       if (fw->logical_in[0] != '\0')
+               add_logical_iniface(r, fw->logical_in, flags);
+
+       if (fw->logical_out[0] != '\0')
+               add_logical_outiface(r, fw->logical_out, flags);
+
+       addr = ether_ntoa((struct ether_addr *) fw->sourcemac);
+       if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+               add_payload(r, offsetof(struct ethhdr, h_source), 6);
+               add_cmp_ptr(r, NFT_CMP_EQ, fw->sourcemac, 6);
+       }
+
+       addr = ether_ntoa((struct ether_addr *) fw->destmac);
+       if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+               add_payload(r, offsetof(struct ethhdr, h_dest), 6);
+               add_cmp_ptr(r, NFT_CMP_EQ, fw->destmac, 6);
+       }
+
+       if (fw->ethproto != 0) {
+               add_payload(r, offsetof(struct ethhdr, h_proto), 2);
+               add_cmp_u16(r, fw->ethproto, NFT_CMP_EQ);
+       }
+
+       return _add_action(r, fw);
+}
+
+static void nft_bridge_parse_meta(struct nft_xt_ctx *ctx,
+                                 struct nft_rule_expr *e, void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+       uint8_t flags = 0;
+       int iface = 0;
+       const void *ifname;
+       uint32_t len;
+
+       iface = parse_meta(e, ctx->meta.key, fw->in, fw->in_mask,
+                          fw->out, fw->out_mask, &flags);
+       if (!iface)
+               goto out;
+
+       switch (ctx->meta.key) {
+       case NFT_META_BRI_IIFNAME:
+               ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+               if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+                       flags |= IPT_INV_VIA_IN;
+
+               memcpy(fw->logical_in, ifname, len);
+
+               if (fw->logical_in[len] == '\0') 
+                       memset(fw->in_mask, 0xff, len);
+               else {
+                       fw->logical_in[len] = '+';
+                       fw->logical_in[len+1] = '\0';
+                       memset(fw->in_mask, 0xff, len + 1);
+               }
+               break;
+       case NFT_META_BRI_OIFNAME:
+               ifname = nft_rule_expr_get(e, NFT_EXPR_CMP_DATA, &len);
+               if (nft_rule_expr_get_u32(e, NFT_EXPR_CMP_OP) == NFT_CMP_NEQ)
+                       flags |= IPT_INV_VIA_OUT;
+
+               memcpy(fw->logical_out, ifname, len);
+
+               if (fw->logical_out[len] == '\0') 
+                       memset(fw->out_mask, 0xff, len);
+               else {
+                       fw->logical_out[len] = '+';
+                       fw->logical_out[len+1] = '\0';
+                       memset(fw->out_mask, 0xff, len + 1);
+               }
+               break;
+       default:
+               break;
+       }
+
+out:
+       fw->invflags |= ipt_to_ebt_flags(flags);
+}
+
+static void nft_bridge_parse_payload(struct nft_xt_ctx *ctx,
+                                    struct nft_rule_expr *e, void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+       unsigned char addr[ETH_ALEN];
+       unsigned short int ethproto;
+       bool inv;
+       int i;
+
+       switch (ctx->payload.offset) {
+       case offsetof(struct ethhdr, h_dest):
+               get_cmp_data(e, addr, sizeof(addr), &inv);
+               for (i = 0; i < ETH_ALEN; i++)
+                       fw->destmac[i] = addr[i];
+               break;
+       case offsetof(struct ethhdr, h_source):
+               get_cmp_data(e, addr, sizeof(addr), &inv);
+               for (i = 0; i < ETH_ALEN; i++)
+                       fw->sourcemac[i] = addr[i];
+               break;
+       case offsetof(struct ethhdr, h_proto):
+               get_cmp_data(e, &ethproto, sizeof(ethproto), &inv);
+               fw->ethproto = ethproto;
+               break;
+       }
+}
+static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
+                                      void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+
+       fw->jumpto = jumpto;
+}
+
+static void nft_bridge_parse_target(struct xtables_target *t, void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+
+       fw->target = t;
+}
+
+void nft_rule_to_xtables_ebt_entry(struct nft_rule *r, struct xtables_ebt_entry *fw)
+{
+       struct nft_rule_expr_iter *iter;
+       struct nft_rule_expr *expr;
+       int family = nft_rule_attr_get_u32(r, NFT_RULE_ATTR_FAMILY);
+       struct nft_xt_ctx ctx = {
+               .state.ebfw = fw,
+               .family = family,
+       };
+
+       iter = nft_rule_expr_iter_create(r);
+       if (iter == NULL)
+               return;
+
+       expr = nft_rule_expr_iter_next(iter);
+       while (expr != NULL) {
+               const char *name =
+                       nft_rule_expr_get_str(expr, NFT_RULE_EXPR_ATTR_NAME);
+
+               if (strcmp(name, "counter") == 0)
+                       nft_parse_counter(expr, &fw->counters);
+               else if (strcmp(name, "payload") == 0)
+                       nft_parse_payload(&ctx, expr);
+               else if (strcmp(name, "meta") == 0)
+                       nft_parse_meta(&ctx, expr);
+               else if (strcmp(name, "cmp") == 0)
+                       nft_parse_cmp(&ctx, expr);
+               else if (strcmp(name, "immediate") == 0)
+                       nft_parse_immediate(&ctx, expr);
+               else if (strcmp(name, "target") == 0)
+                       nft_parse_target(&ctx, expr);
+
+               expr = nft_rule_expr_iter_next(iter);
+       }
+
+       nft_rule_expr_iter_destroy(iter);
+
+       if (fw->target != NULL)
+               fw->jumpto = fw->target->name;
+       else if (fw->jumpto != NULL)
+               fw->target = xtables_find_target(fw->jumpto, XTF_TRY_LOAD);
+       else
+               fw->jumpto = "";
+}
+
+static void print_iface(const char *iface)
+{
+       char *c;
+
+       if ((c = strchr(iface, IF_WILDCARD)))
+               *c = '+';
+       printf("%s ", iface);
+       if (c)
+               *c = IF_WILDCARD;
+}
+
+static void nft_bridge_print_header(unsigned int format, const char *chain,
+                                   const char *pol,
+                                   const struct xt_counters *counters,
+                                   bool basechain, uint32_t refs)
+{
+       /* FIXME: Giuseppe replace this with ebtables format */
+       print_header(format, chain, pol, counters, basechain, refs);
+}
+
+static void
+nft_bridge_print_firewall(struct nft_rule *r, unsigned int num,
+                      unsigned int format)
+{
+       struct xtables_ebt_entry fw = {};
+       char *addr;
+
+       nft_rule_to_xtables_ebt_entry(r, &fw);
+
+       if (format & FMT_LINENUMBERS)
+               printf("%d ", num);
+
+       /* Dont print anything about the protocol if no protocol was
+        * specified, obviously this means any protocol will do. */
+       if (fw.ethproto != 0) {
+               printf("-p ");
+               if (fw.invflags & EBT_IPROTO)
+                       printf("! ");
+               if (fw.bitmask & EBT_802_3)
+                       printf("Length ");
+               else {
+                       struct ethertypeent *ent;
+
+                       ent = getethertypebynumber(ntohs(fw.ethproto));
+                       if (!ent)
+                               printf("0x%x ", ntohs(fw.ethproto));
+                       else
+                               printf("%s ", ent->e_name);
+               }
+       }
+
+       addr = ether_ntoa((struct ether_addr *) fw.sourcemac);
+       if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+               printf("-s ");
+               if (fw.invflags & EBT_ISOURCE)
+                       printf("! ");
+               ebt_print_mac_and_mask(fw.sourcemac, fw.sourcemsk);
+               printf(" ");
+       }
+
+       addr = ether_ntoa((struct ether_addr *) fw.destmac);
+       if (strcmp(addr, "0:0:0:0:0:0") != 0) {
+               printf("-d ");
+               if (fw.invflags & EBT_IDEST)
+                       printf("! ");
+               ebt_print_mac_and_mask(fw.destmac, fw.destmsk);
+               printf(" ");
+       }
+
+       if (fw.in[0] != '\0') {
+               printf("-i ");
+               if (fw.invflags & EBT_IIN)
+                       printf("! ");
+               print_iface(fw.in);
+       }
+
+       if (fw.logical_in[0] != '\0') {
+               printf("--logical-in ");
+               if (fw.invflags & EBT_ILOGICALIN)
+                       printf("! ");
+               print_iface(fw.logical_in);
+       }
+
+       if (fw.logical_out[0] != '\0') {
+               printf("--logical-out ");
+               if (fw.invflags & EBT_ILOGICALOUT)
+                       printf("! ");
+               print_iface(fw.logical_out);
+       }
+
+       if (fw.out[0] != '\0') {
+               printf("-o ");
+               if (fw.invflags & EBT_IOUT)
+                       printf("! ");
+               print_iface(fw.out);
+       }
+
+       /* old code to adapt
+       m_l = hlp->m_list;
+       while (m_l) {
+               m = ebt_find_match(m_l->m->u.name);
+               if (!m)
+                       ebt_print_bug("Match not found");
+               m->print(hlp, m_l->m);
+               m_l = m_l->next;
+       }
+       w_l = hlp->w_list;
+       while (w_l) {
+               w = ebt_find_watcher(w_l->w->u.name);
+               if (!w)
+                       ebt_print_bug("Watcher not found");
+               w->print(hlp, w_l->w);
+               w_l = w_l->next;
+       }*/
+       printf("-j ");
+       if (!(format & FMT_NOTARGET))
+               printf("%s", fw.jumpto);
+
+       if (fw.target != NULL) {
+               if (fw.target->print != NULL) {
+                       fw.target->print(&fw, fw.target->t,
+                                        format & FMT_NUMERIC);
+               }
+       }
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static bool nft_bridge_is_same(const void *data_a,
+                              const void *data_b)
+{
+       const struct xtables_ebt_entry *a = data_a;
+       const struct xtables_ebt_entry *b = data_b;
+       int i;
+
+       if (a->ethproto != b->ethproto
+           /*|| a->flags != b->flags*/
+           || a->invflags != b->invflags) {
+               DEBUGP("different proto/flags/invflags\n");
+               return false;
+       }
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               if (a->sourcemac[i] != b->sourcemac[i]) {
+                       DEBUGP("different source mac %x, %x (%d)\n",
+                       a->sourcemac[i] & 0xff, b->sourcemac[i] & 0xff, i);
+                       return false;
+               }
+
+               if (a->destmac[i] != b->destmac[i]) {
+                       DEBUGP("different destination mac %x, %x (%d)\n",
+                       a->destmac[i] & 0xff, b->destmac[i] & 0xff, i);
+                       return false;
+               }
+       }
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->logical_in[i] != b->logical_in[i]) {
+                       DEBUGP("different logical iniface %x, %x (%d)\n",
+                       a->logical_in[i] & 0xff, b->logical_in[i] & 0xff, i);
+                       return false;
+               }
+
+               if (a->logical_out[i] != b->logical_out[i]) {
+                       DEBUGP("different logical outiface %x, %x (%d)\n",
+                       a->logical_out[i] & 0xff, b->logical_out[i] & 0xff, i);
+                       return false;
+               }
+       }
+
+       return is_same_interfaces((char *)a->in,
+                                 (char *)a->out,
+                                 a->in_mask,
+                                 a->out_mask,
+                                 (char *)b->in,
+                                 (char *)b->out,
+                                 b->in_mask,
+                                 b->out_mask);
+}
+
+static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
+                                void *data)
+{
+       struct xtables_ebt_entry *fw = data;
+       struct xtables_ebt_entry this = {};
+
+       nft_rule_to_xtables_ebt_entry(r, &this);
+
+       DEBUGP("comparing with... ");
+
+       if (!ops->is_same(fw, &this))
+               return false;
+
+       if (!compare_matches(fw->matches, this.matches)) {
+               DEBUGP("Different matches\n");
+               return false;
+       }
+
+       if (!compare_targets(fw->target, this.target)) {
+               DEBUGP("Different target\n");
+               return false;
+       }
+
+       if (strcmp(fw->jumpto, this.jumpto) != 0) {
+               DEBUGP("Different verdict\n");
+               return false;
+       }
+
+       return true;
+}
+
+struct nft_family_ops nft_family_ops_bridge = {
+       .add                    = nft_bridge_add,
+       .is_same                = nft_bridge_is_same,
+       .print_payload          = NULL,
+       .parse_meta             = nft_bridge_parse_meta,
+       .parse_payload          = nft_bridge_parse_payload,
+       .parse_immediate        = nft_bridge_parse_immediate,
+       .print_header           = nft_bridge_print_header,
+       .print_firewall         = nft_bridge_print_firewall,
+       .post_parse             = NULL,
+       .rule_find              = nft_bridge_rule_find,
+       .parse_target           = nft_bridge_parse_target,
+};
index d4a54beee49735192b00649183a828c46aa63615..9ca2e35c102c48c70d034f4d9aee6432940b391c 100644 (file)
@@ -32,6 +32,7 @@
 extern struct nft_family_ops nft_family_ops_ipv4;
 extern struct nft_family_ops nft_family_ops_ipv6;
 extern struct nft_family_ops nft_family_ops_arp;
+extern struct nft_family_ops nft_family_ops_bridge;
 
 void add_meta(struct nft_rule *r, uint32_t key)
 {
@@ -206,7 +207,7 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
        return true;
 }
 
-void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
+int parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
                unsigned char *iniface_mask, char *outiface,
                unsigned char *outiface_mask, uint8_t *invflags)
 {
@@ -264,9 +265,10 @@ void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
                }
                break;
        default:
-               DEBUGP("unknown meta key %d\n", key);
-               break;
+               return -1;
        }
+
+       return 0;
 }
 
 static void *nft_get_data(struct nft_xt_ctx *ctx)
@@ -277,6 +279,8 @@ static void *nft_get_data(struct nft_xt_ctx *ctx)
                return ctx->state.cs;
        case NFPROTO_ARP:
                return ctx->state.cs_arp;
+       case NFPROTO_BRIDGE:
+               return ctx->state.ebfw;
        default:
                /* Should not happen */
                return NULL;
@@ -734,6 +738,8 @@ struct nft_family_ops *nft_family_ops_lookup(int family)
                return &nft_family_ops_ipv6;
        case NFPROTO_ARP:
                return &nft_family_ops_arp;
+       case NFPROTO_BRIDGE:
+               return &nft_family_ops_bridge;
        default:
                break;
        }
@@ -741,8 +747,8 @@ struct nft_family_ops *nft_family_ops_lookup(int family)
        return NULL;
 }
 
-static bool
-compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2)
+bool compare_matches(struct xtables_rule_match *mt1,
+                    struct xtables_rule_match *mt2)
 {
        struct xtables_rule_match *mp1;
        struct xtables_rule_match *mp2;
index 1b16d4e082bb2a1ee37c3033e3d0c5ac35a45d5e..97d41df8d174e782678b65fbdd292e4ea77f47eb 100644 (file)
@@ -48,6 +48,7 @@ struct nft_xt_ctx {
        union {
                struct iptables_command_state *cs;
                struct arptables_command_state *cs_arp;
+               struct xtables_ebt_entry *ebfw;
        } state;
        struct nft_rule_expr_iter *iter;
        int family;
@@ -122,7 +123,7 @@ bool is_same_interfaces(const char *a_iniface, const char *a_outiface,
                        unsigned const char *b_iniface_mask,
                        unsigned const char *b_outiface_mask);
 
-void parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
+int parse_meta(struct nft_rule_expr *e, uint8_t key, char *iniface,
                unsigned char *iniface_mask, char *outiface,
                unsigned char *outiface_mask, uint8_t *invflags);
 void print_proto(uint16_t proto, int invert);
@@ -165,6 +166,7 @@ struct nft_handle;
 bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nft_rule *r,
                         struct iptables_command_state *cs);
 
+bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
 bool compare_targets(struct xtables_target *tg1, struct xtables_target *tg2);
 
 struct addr_mask {
index 568faa1914772ef1dfa32d84cb9fe8d4811b486a..804ebe97ee9c26285cf8b8a6e3b4c5cbbb5ab6e1 100644 (file)
@@ -469,6 +469,57 @@ struct builtin_table xtables_arp[TABLES_MAX] = {
        },
 };
 
+#include <linux/netfilter_bridge.h>
+
+struct builtin_table xtables_bridge[TABLES_MAX] = {
+       [FILTER] = {
+               .name = "filter",
+               .chains = {
+                       {
+                               .name   = "INPUT",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_FILTER_BRIDGED,
+                               .hook   = NF_BR_LOCAL_IN,
+                       },
+                       {
+                               .name   = "FORWARD",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_FILTER_BRIDGED,
+                               .hook   = NF_BR_FORWARD,
+                       },
+                       {
+                               .name   = "OUTPUT",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_FILTER_BRIDGED,
+                               .hook   = NF_BR_LOCAL_OUT,
+                       },
+               },
+       },
+       [NAT] = {
+               .name = "nat",
+               .chains = {
+                       {
+                               .name   = "PREROUTING",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_NAT_DST_BRIDGED,
+                               .hook   = NF_BR_PRE_ROUTING,
+                       },
+                       {
+                               .name   = "OUTPUT",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_NAT_DST_OTHER,
+                               .hook   = NF_BR_LOCAL_OUT,
+                       },
+                       {
+                               .name   = "POSTROUTING",
+                               .type   = "filter",
+                               .prio   = NF_BR_PRI_NAT_SRC,
+                               .hook   = NF_BR_POST_ROUTING,
+                       },
+               },
+       },
+};
+
 int nft_table_add(struct nft_handle *h, struct nft_table *t, uint16_t flags)
 {
        char buf[MNL_SOCKET_BUFFER_SIZE];
@@ -587,7 +638,7 @@ nft_table_builtin_find(struct nft_handle *h, const char *table)
 
        for (i=0; i<TABLES_MAX; i++) {
                if (h->tables[i].name == NULL)
-                       break;
+                       continue;
 
                if (strcmp(h->tables[i].name, table) != 0)
                        continue;
index 9f6561f501c4c9f049f7d2634a498c5a95f05955..4f0b733cbd1bb0b64589efa63bfb8affd707c9db 100644 (file)
@@ -41,6 +41,7 @@ struct nft_handle {
 
 extern struct builtin_table xtables_ipv4[TABLES_MAX];
 extern struct builtin_table xtables_arp[TABLES_MAX];
+extern struct builtin_table xtables_bridge[TABLES_MAX];
 
 int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
             int (*cb)(const struct nlmsghdr *nlh, void *data),
@@ -135,6 +136,8 @@ const char *nft_strerror(int err);
 int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
 /* For xtables-arptables.c */
 int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table);
+/* For xtables-eb.c */
+int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table);
 
 /*
  * Parse config for tables and chain helper functions
@@ -167,4 +170,13 @@ int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
 
 void nft_rule_to_arpt_entry(struct nft_rule *r, struct arpt_entry *fw);
 
+/*
+ * BRIDGE
+ */
+
+#include "xtables-ebtables.h"
+
+struct xtables_ebt_entry;
+
+void nft_rule_to_xtables_ebt_entry(struct nft_rule *r, struct xtables_ebt_entry *fw);
 #endif
index 478105244ec7ee04dc77ba136fa4d46fb2bef3ff..ed8ad07ff99b4342a99fc622188a2b1a7e173429 100644 (file)
@@ -28,6 +28,7 @@ static const struct subcommand multi_subcommands[] = {
        {"ip6tables-compat-restore",    xtables_ip6_restore_main},
        {"arptables",                   xtables_arp_main},
        {"arptables-compat",            xtables_arp_main},
+       {"ebtables-compat",             xtables_eb_main},
        {"xtables-config",              xtables_config_main},
        {"xtables-events",              xtables_events_main},
        {NULL},
diff --git a/iptables/xtables-eb-standalone.c b/iptables/xtables-eb-standalone.c
new file mode 100644 (file)
index 0000000..6ec418c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     arptables -- IP firewall administration for kernels with
+ *     firewall table (aimed for the 2.3 kernels)
+ *
+ *     See the accompanying manual page arptables(8) for information
+ *     about proper usage of this program.
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <xtables.h>
+#include "nft.h"
+
+#include "xtables-multi.h"
+
+extern struct xtables_globals xtables_globals;
+extern const char *program_version, *program_name;
+
+static const struct xtables_afinfo afinfo_bridge = {
+        .kmod          = "eb_tables",
+        .proc_exists   = "/proc/net/eb_tables_names",
+        .libprefix     = "libeb_",
+        .family        = NFPROTO_BRIDGE,
+        .ipproto       = IPPROTO_IP,
+        .so_rev_match  = -1,
+        .so_rev_target = -1,
+};
+
+int xtables_eb_main(int argc, char *argv[])
+{
+       int ret;
+       char *table = "filter";
+       struct nft_handle h = {
+               .family = NFPROTO_BRIDGE,
+       };
+
+       xtables_globals.program_name = "ebtables";
+       /* This code below could be replaced by xtables_init_all, which
+        * doesn't support NFPROTO_BRIDGE yet.
+        */
+       xtables_init();
+       afinfo = &afinfo_bridge;
+       ret = xtables_set_params(&xtables_globals);
+       if (ret < 0) {
+               fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+                               xtables_globals.program_name,
+                               xtables_globals.program_version);
+               exit(1);
+       }
+
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+       init_extensions();
+#endif
+
+       ret = do_commandeb(&h, argc, argv, &table);
+       if (ret)
+               ret = nft_commit(&h);
+
+       exit(!ret);
+}
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
new file mode 100644 (file)
index 0000000..73a38db
--- /dev/null
@@ -0,0 +1,1196 @@
+/*
+ * ebtables.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code was stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <xtables.h>
+
+#include <ebtables/ethernetdb.h>
+#include "xshared.h"
+#include "nft.h"
+
+extern struct xtables_globals xtables_globals;
+#define prog_name xtables_globals.program_name
+#define prog_vers xtables_globals.program_version
+
+/*
+ * From include/ebtables_u.h
+ */
+#define EXEC_STYLE_PRG    0
+#define EXEC_STYLE_DAEMON 1
+
+/*
+ * From useful_functions.c
+ */
+
+/* 0: default
+ * 1: the inverse '!' of the option has already been specified */
+int ebt_invert = 0;
+
+unsigned char eb_mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
+unsigned char eb_msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+unsigned char eb_mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char eb_msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char eb_mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char eb_msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char eb_mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
+unsigned char eb_msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255};
+
+int ebt_get_mac_and_mask(const char *from, unsigned char *to,
+  unsigned char *mask)
+{
+       char *p;
+       int i;
+       struct ether_addr *addr = NULL;
+
+       if (strcasecmp(from, "Unicast") == 0) {
+               memcpy(to, eb_mac_type_unicast, ETH_ALEN);
+               memcpy(mask, eb_msk_type_unicast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "Multicast") == 0) {
+               memcpy(to, eb_mac_type_multicast, ETH_ALEN);
+               memcpy(mask, eb_msk_type_multicast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "Broadcast") == 0) {
+               memcpy(to, eb_mac_type_broadcast, ETH_ALEN);
+               memcpy(mask, eb_msk_type_broadcast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "BGA") == 0) {
+               memcpy(to, eb_mac_type_bridge_group, ETH_ALEN);
+               memcpy(mask, eb_msk_type_bridge_group, ETH_ALEN);
+               return 0;
+       }
+       if ( (p = strrchr(from, '/')) != NULL) {
+               *p = '\0';
+               if (!(addr = ether_aton(p + 1)))
+                       return -1;
+               memcpy(mask, addr, ETH_ALEN);
+       } else
+               memset(mask, 0xff, ETH_ALEN);
+       if (!(addr = ether_aton(from)))
+               return -1;
+       memcpy(to, addr, ETH_ALEN);
+       for (i = 0; i < ETH_ALEN; i++)
+               to[i] &= mask[i];
+       return 0;
+}
+
+/* This is a replacement for the ebt_check_option2() macro.
+ *
+ * Make sure the same option wasn't specified twice. This is used in the parse
+ * functions of the extensions and ebtables.c.
+ */
+static void ebt_check_option2(unsigned int *flags, unsigned int mask)
+{
+       if (*flags & mask)
+               xtables_error(PARAMETER_PROBLEM,
+                             "Multiple use of same option not allowed");
+       *flags |= mask;
+}
+
+static int ebt_check_inverse2(const char option[], int argc, char **argv)
+{
+       if (!option)
+               return ebt_invert;
+       if (strcmp(option, "!") == 0) {
+               if (ebt_invert == 1)
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Double use of '!' not allowed");
+               if (optind >= argc)
+                       optarg = NULL;
+               else
+                       optarg = argv[optind];
+               optind++;
+               ebt_invert = 1;
+               return 1;
+       }
+       return ebt_invert;
+}
+
+/*
+ * From libebtc.c
+ */
+
+/* The four target names, from libebtc.c */
+const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
+{
+       "ACCEPT",
+       "DROP",
+       "CONTINUE",
+       "RETURN",
+};
+
+/* Prints all registered extensions */
+static void ebt_list_extensions(const struct xtables_target *t,
+                               const struct xtables_rule_match *m)
+{
+       printf("%s v%s\n", prog_name, prog_vers);
+       printf("Loaded userspace extensions:\n");
+       /*printf("\nLoaded tables:\n");
+        while (tbl) {
+               printf("%s\n", tbl->name);
+                tbl = tbl->next;
+       }*/
+       printf("\nLoaded targets:\n");
+        for (t = xtables_targets; t; t = t->next) {
+               printf("%s\n", t->name);
+       }
+       printf("\nLoaded matches:\n");
+        for (; m != NULL; m = m->next)
+               printf("%s\n", m->match->name);
+       /*printf("\nLoaded watchers:\n");
+        while (w) {
+               printf("%s\n", w->name);
+                w = w->next;
+       }*/
+}
+
+/*
+ * Glue code to use libxtables
+ */
+static int parse_rule_number(const char *rule)
+{
+       unsigned int rule_nr;
+
+       if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
+               xtables_error(PARAMETER_PROBLEM,
+                             "Invalid rule number `%s'", rule);
+
+       return rule_nr;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               xtables_error(PARAMETER_PROBLEM,
+                             "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > EBT_CHAIN_MAXNAMELEN)
+               xtables_error(PARAMETER_PROBLEM,
+                             "Invalid target '%s' (%d chars max)",
+                             targetname, EBT_CHAIN_MAXNAMELEN);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static int
+append_entry(struct nft_handle *h,
+            const char *chain,
+            const char *table,
+            struct xtables_ebt_entry *fw,
+            int rule_nr,
+            bool verbose, bool append)
+{
+       int ret = 1;
+
+       if (append)
+               ret = nft_rule_append(h, chain, table, fw, 0, verbose);
+       else
+               ret = nft_rule_insert(h, chain, table, fw, rule_nr, verbose);
+
+       return ret;
+}
+
+static int
+delete_entry(struct nft_handle *h,
+            const char *chain,
+            const char *table,
+            struct xtables_ebt_entry *fw,
+            int rule_nr,
+            int rule_nr_end,
+            bool verbose)
+{
+       int ret = 1;
+
+       if (rule_nr == -1)
+               ret = nft_rule_delete(h, chain, table, fw, verbose);
+       else {
+               do {
+                       ret = nft_rule_delete_num(h, chain, table,
+                                                 rule_nr, verbose);
+                       rule_nr++;
+               } while (rule_nr < rule_nr_end);
+       }
+
+       return ret;
+}
+
+static int get_current_chain(const char *chain)
+{
+       if (strcmp(chain, "PREROUTING") == 0)
+               return NF_BR_PRE_ROUTING;
+       else if (strcmp(chain, "INPUT") == 0)
+               return NF_BR_LOCAL_IN;
+       else if (strcmp(chain, "FORWARD") == 0)
+               return NF_BR_FORWARD;
+       else if (strcmp(chain, "OUTPUT") == 0)
+               return NF_BR_LOCAL_OUT;
+       else if (strcmp(chain, "POSTROUTING") == 0)
+               return NF_BR_POST_ROUTING;
+
+       return -1;
+}
+
+/*
+ * The original ebtables parser
+ */
+
+/* Checks whether a command has already been specified */
+#define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
+
+#define OPT_COMMAND    0x01
+#define OPT_TABLE      0x02
+#define OPT_IN         0x04
+#define OPT_OUT                0x08
+#define OPT_JUMP       0x10
+#define OPT_PROTOCOL   0x20
+#define OPT_SOURCE     0x40
+#define OPT_DEST       0x80
+#define OPT_ZERO       0x100
+#define OPT_LOGICALIN  0x200
+#define OPT_LOGICALOUT 0x400
+#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */
+#define OPT_COUNT      0x1000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_INCR   0x2000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_DECR   0x4000 /* This value is also defined in libebtc.c */
+
+/* Default command line options. Do not mess around with the already
+ * assigned numbers unless you know what you are doing */
+static struct option ebt_original_options[] =
+{
+       { "append"         , required_argument, 0, 'A' },
+       { "insert"         , required_argument, 0, 'I' },
+       { "delete"         , required_argument, 0, 'D' },
+       { "list"           , optional_argument, 0, 'L' },
+       { "Lc"             , no_argument      , 0, 4   },
+       { "Ln"             , no_argument      , 0, 5   },
+       { "Lx"             , no_argument      , 0, 6   },
+       { "Lmac2"          , no_argument      , 0, 12  },
+       { "zero"           , optional_argument, 0, 'Z' },
+       { "flush"          , optional_argument, 0, 'F' },
+       { "policy"         , required_argument, 0, 'P' },
+       { "in-interface"   , required_argument, 0, 'i' },
+       { "in-if"          , required_argument, 0, 'i' },
+       { "logical-in"     , required_argument, 0, 2   },
+       { "logical-out"    , required_argument, 0, 3   },
+       { "out-interface"  , required_argument, 0, 'o' },
+       { "out-if"         , required_argument, 0, 'o' },
+       { "version"        , no_argument      , 0, 'V' },
+       { "help"           , no_argument      , 0, 'h' },
+       { "jump"           , required_argument, 0, 'j' },
+       { "set-counters"   , required_argument, 0, 'c' },
+       { "change-counters", required_argument, 0, 'C' },
+       { "proto"          , required_argument, 0, 'p' },
+       { "protocol"       , required_argument, 0, 'p' },
+       { "db"             , required_argument, 0, 'b' },
+       { "source"         , required_argument, 0, 's' },
+       { "src"            , required_argument, 0, 's' },
+       { "destination"    , required_argument, 0, 'd' },
+       { "dst"            , required_argument, 0, 'd' },
+       { "table"          , required_argument, 0, 't' },
+       { "modprobe"       , required_argument, 0, 'M' },
+       { "new-chain"      , required_argument, 0, 'N' },
+       { "rename-chain"   , required_argument, 0, 'E' },
+       { "delete-chain"   , optional_argument, 0, 'X' },
+       { "atomic-init"    , no_argument      , 0, 7   },
+       { "atomic-commit"  , no_argument      , 0, 8   },
+       { "atomic-file"    , required_argument, 0, 9   },
+       { "atomic-save"    , no_argument      , 0, 10  },
+       { "init-table"     , no_argument      , 0, 11  },
+       { "concurrent"     , no_argument      , 0, 13  },
+       { 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+/*
+ * More glue code.
+ */
+static struct xtables_target *command_jump(struct xtables_ebt_entry *fw,
+                                          const char *jumpto)
+{
+       struct xtables_target *target;
+       size_t size;
+
+       /* XTF_TRY_LOAD (may be chain name) */
+       target = xtables_find_target(jumpto, XTF_TRY_LOAD);
+
+       if (!target)
+               return NULL;
+
+       size = XT_ALIGN(sizeof(struct xt_entry_target))
+               + target->size;
+
+       target->t = xtables_calloc(1, size);
+       target->t->u.target_size = size;
+       strncpy(target->t->u.user.name, jumpto, sizeof(target->t->u.user.name));
+       target->t->u.user.name[sizeof(target->t->u.user.name)-1] = '\0';
+       target->t->u.user.revision = target->revision;
+
+       xs_init_target(target);
+
+       if (target->x6_options != NULL)
+               ebt_options = xtables_options_xfrm(xtables_globals.orig_opts,
+                                           ebt_options, target->x6_options,
+                                           &target->option_offset);
+       else
+               ebt_options = xtables_merge_options(xtables_globals.orig_opts,
+                                            ebt_options, target->extra_opts,
+                                            &target->option_offset);
+
+       return target;
+}
+
+static void print_help(const struct xtables_target *t,
+                      const struct xtables_rule_match *m, const char *table)
+{
+       printf("%s %s\n", prog_name, prog_vers);
+       printf(
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[NX] [chain]\n"
+"ebtables -E old-chain-name new-chain-name\n\n"
+"Commands:\n"
+"--append -A chain             : append to chain\n"
+"--delete -D chain             : delete matching rule from chain\n"
+"--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
+"--change-counters -C chain\n"
+"          [rulenum] pcnt bcnt : change counters of existing rule\n"
+"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+"--list   -L [chain]           : list the rules in a chain or in all chains\n"
+"--flush  -F [chain]           : delete all rules in chain or in all chains\n"
+"--init-table                  : replace the kernel table with the initial table\n"
+"--zero   -Z [chain]           : put counters on zero in chain or in all chains\n"
+"--policy -P chain target      : change policy on chain to target\n"
+"--new-chain -N chain          : create a user defined chain\n"
+"--rename-chain -E old new     : rename a chain\n"
+"--delete-chain -X [chain]     : delete a user defined chain\n"
+"--atomic-commit               : update the kernel w/t table contained in <FILE>\n"
+"--atomic-init                 : put the initial kernel table into <FILE>\n"
+"--atomic-save                 : put the current kernel table into <FILE>\n"
+"--atomic-file file            : set <FILE> to file\n\n"
+"Options:\n"
+"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+"--src    -s [!] address[/mask]: source mac address\n"
+"--dst    -d [!] address[/mask]: destination mac address\n"
+"--in-if  -i [!] name[+]       : network input interface name\n"
+"--out-if -o [!] name[+]       : network output interface name\n"
+"--logical-in  [!] name[+]     : logical bridge input interface name\n"
+"--logical-out [!] name[+]     : logical bridge output interface name\n"
+"--set-counters -c chain\n"
+"          pcnt bcnt           : set the counters of the to be added rule\n"
+"--modprobe -M program         : try to insert modules using this program\n"
+"--concurrent                  : use a file lock to support concurrent scripts\n"
+"--version -V                  : print package version\n\n"
+"Environment variable:\n"
+/*ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its value"*/
+"\n\n");
+       for (; m != NULL; m = m->next) {
+               printf("\n");
+               m->match->help();
+       }
+       if (t != NULL) {
+               printf("\n");
+               t->help();
+       }
+
+//     if (table->help)
+//             table->help(ebt_hooknames);
+}
+
+/* Execute command L */
+static int list_rules(struct nft_handle *h, const char *chain, const char *table,
+                     int rule_nr, int verbose, int numeric, int expanded,
+                     int linenumbers)
+{
+       unsigned int format;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       return nft_rule_list(h, chain, table, rule_nr, format);
+}
+
+static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
+{
+       char *colon = strchr(argv, ':'), *buffer;
+
+       if (colon) {
+               *colon = '\0';
+               if (*(colon + 1) == '\0')
+                       *rule_nr_end = -1; /* Until the last rule */
+               else {
+                       *rule_nr_end = strtol(colon + 1, &buffer, 10);
+                       if (*buffer != '\0' || *rule_nr_end == 0)
+                               return -1;
+               }
+       }
+       if (colon == argv)
+               *rule_nr = 1; /* Beginning with the first rule */
+       else {
+               *rule_nr = strtol(argv, &buffer, 10);
+               if (*buffer != '\0' || *rule_nr == 0)
+                       return -1;
+       }
+       if (!colon)
+               *rule_nr_end = *rule_nr;
+       return 0;
+}
+
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it (too annoying to take the increased
+ * counters in the kernel into account). */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style, struct xtables_ebt_entry *fw)
+{
+       char *buffer;
+       int ret = 0;
+
+       if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
+           (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0'  && argv[optind + 1][1] > '9')))
+               xtables_error(PARAMETER_PROBLEM,
+                             "The command -C needs at least 2 arguments");
+       if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
+               if (optind + 3 != argc)
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
+               if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Something is wrong with the rule number specification '%s'", argv[optind]);
+               optind++;
+       }
+
+       if (argv[optind][0] == '+') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+               ret += 1;
+               fw->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+       } else if (argv[optind][0] == '-') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+               ret += 2;
+               fw->counters.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+       } else
+               fw->counters.pcnt = strtoull(argv[optind], &buffer, 10);
+
+       if (*buffer != '\0')
+               goto invalid;
+       optind++;
+       if (argv[optind][0] == '+') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+                       goto daemon_incr;
+               ret += 3;
+               fw->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+       } else if (argv[optind][0] == '-') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+                       goto daemon_decr;
+               ret += 6;
+               fw->counters.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+       } else
+               fw->counters.bcnt = strtoull(argv[optind], &buffer, 10);
+
+       if (*buffer != '\0')
+               goto invalid;
+       optind++;
+       return ret;
+invalid:
+       xtables_error(PARAMETER_PROBLEM,"Packet counter '%s' invalid", argv[optind]);
+}
+
+static int parse_iface(char *iface, char *option)
+{
+       char *c;
+
+       if ((c = strchr(iface, '+'))) {
+               if (*(c + 1) != '\0') {
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Spurious characters after '+' wildcard for '%s'", option);
+                       return -1;
+               } else
+                       *c = IF_WILDCARD;
+       }
+       return 0;
+}
+
+/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
+int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table)
+{
+       char *buffer;
+       int c, i;
+       int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
+       int chcounter = 0; /* Needed for -C */
+       int rule_nr = 0;
+       int rule_nr_end = 0;
+       int ret = 0;
+       unsigned int flags = 0;
+       struct xtables_target *t;
+       struct xtables_ebt_entry fw;
+       char command = 'h';
+       const char *chain = NULL;
+       const char *newname = NULL;
+       const char *policy = NULL;
+       int exec_style = EXEC_STYLE_PRG;
+       int selected_chain = -1;
+
+       memset(&fw, 0, sizeof(fw));
+
+       if (nft_init(h, xtables_bridge) < 0)
+               xtables_error(OTHER_PROBLEM,
+                             "Could not initialize nftables layer.");
+
+       h->ops = nft_family_ops_lookup(h->family);
+       if (h->ops == NULL)
+               xtables_error(PARAMETER_PROBLEM, "Unknown family");
+
+       for (t = xtables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Getopt saves the day */
+       while ((c = getopt_long(argc, argv,
+          "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, NULL)) != -1) {
+               switch (c) {
+
+               case 'A': /* Add a rule */
+               case 'D': /* Delete a rule */
+               case 'C': /* Change counters */
+               case 'P': /* Define policy */
+               case 'I': /* Insert a rule */
+               case 'N': /* Make a user defined chain */
+               case 'E': /* Rename chain */
+               case 'X': /* Delete chain */
+                       /* We allow -N chainname -P policy */
+                       if (command == 'N' && c == 'P') {
+                               command = c;
+                               optind--; /* No table specified */
+                               goto handle_P;
+                       }
+                       if (OPT_COMMANDS)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Multiple commands are not allowed");
+
+                       command = c;
+                       chain = optarg;
+                       selected_chain = get_current_chain(chain);
+                       flags |= OPT_COMMAND;
+                       /*if (!(replace->flags & OPT_KERNELDATA))
+                               ebt_get_kernel_table(replace, 0);*/
+                       /*if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
+                               ebt_print_error2("No chain name specified");*/
+                       if (c == 'N') {
+                               ret = nft_chain_user_add(h, chain, *table);
+                               break;
+                       } else if (c == 'X') {
+                               ret = nft_chain_user_del(h, chain, *table);
+                               break;
+                       }
+
+                       if (c == 'E') {
+                               ret = nft_chain_user_rename(h, chain, *table, newname);
+                               break;
+                       } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
+                               if (optind != argc - 1)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                        "No extra options allowed with -D start_nr[:end_nr]");
+                               if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                        "Problem with the specified rule number(s) '%s'", argv[optind]);
+                               optind++;
+                       } else if (c == 'C') {
+                               if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style, &fw)) == -1)
+                                       return -1;
+                       } else if (c == 'I') {
+                               if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
+                                       rule_nr = 1;
+                               else {
+                                       rule_nr = parse_rule_number(argv[optind]);
+                                       optind++;
+                               }
+                       } else if (c == 'P') {
+handle_P:
+                               if (optind >= argc)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "No policy specified");
+                               for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+                                       if (!strcmp(argv[optind], ebt_standard_targets[i])) {
+                                               policy = argv[optind];
+                                               printf("policy: %s\n", policy);
+                                               if (-i-1 == EBT_CONTINUE)
+                                                       xtables_error(PARAMETER_PROBLEM,
+                                                                     "Wrong policy '%s'",
+                                                                     argv[optind]);
+                                               break;
+                                       }
+                               if (i == NUM_STANDARD_TARGETS)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Unknown policy '%s'", argv[optind]);
+                               optind++;
+                       }
+                       break;
+               case 'L': /* List */
+               case 'F': /* Flush */
+               case 'Z': /* Zero counters */
+                       if (c == 'Z') {
+                               if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
+print_zero:
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Command -Z only allowed together with command -L");
+                               flags |= OPT_ZERO;
+                       } else {
+                               if (flags & OPT_COMMAND)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Multiple commands are not allowed");
+                               command = c;
+                               flags |= OPT_COMMAND;
+                               if (flags & OPT_ZERO && c != 'L')
+                                       goto print_zero;
+                       }
+
+#ifdef SILENT_DAEMON
+                       if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "-L not supported in daemon mode");
+#endif
+
+                       /*if (!(replace->flags & OPT_KERNELDATA))
+                               ebt_get_kernel_table(replace, 0);
+                       i = -1;
+                       if (optind < argc && argv[optind][0] != '-') {
+                               if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
+                                       ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+                               optind++;
+                       }
+                       if (i != -1) {
+                               if (c == 'Z')
+                                       zerochain = i;
+                               else
+                                       replace->selected_chain = i;
+                       }*/
+                       break;
+               case 'V': /* Version */
+                       if (OPT_COMMANDS)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Multiple commands are not allowed");
+                       command = 'V';
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "%s %s\n", prog_name, prog_vers);
+                       printf("%s %s\n", prog_name, prog_vers);
+                       exit(0);
+               case 'h': /* Help */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "-h not supported in daemon mode");
+#endif
+                       if (OPT_COMMANDS)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Multiple commands are not allowed");
+                       command = 'h';
+
+                       /* All other arguments should be extension names */
+                       while (optind < argc) {
+                               /*struct ebt_u_match *m;
+                               struct ebt_u_watcher *w;*/
+
+                               if (!strcasecmp("list_extensions", argv[optind])) {
+                                       ebt_list_extensions(xtables_targets, fw.matches);
+                                       exit(0);
+                               }
+                               /*if ((m = ebt_find_match(argv[optind])))
+                                       ebt_add_match(new_entry, m);
+                               else if ((w = ebt_find_watcher(argv[optind])))
+                                       ebt_add_watcher(new_entry, w);
+                               else {*/
+                                       if (!(t = xtables_find_target(argv[optind], XTF_TRY_LOAD)))
+                                               xtables_error(PARAMETER_PROBLEM,"Extension '%s' not found", argv[optind]);
+                                       if (flags & OPT_JUMP)
+                                               xtables_error(PARAMETER_PROBLEM,"Sorry, you can only see help for one target extension at a time");
+                                       flags |= OPT_JUMP;
+                                       fw.target = t;
+                               //}
+                               optind++;
+                       }
+                       break;
+               case 't': /* Table */
+                       if (OPT_COMMANDS)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Please put the -t option first");
+                       ebt_check_option2(&flags, OPT_TABLE);
+                       if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Table name length cannot exceed %d characters",
+                                             EBT_TABLE_MAXNAMELEN - 1);
+                       *table = optarg;
+                       break;
+               case 'i': /* Input interface */
+               case 2  : /* Logical input interface */
+               case 'o': /* Output interface */
+               case 3  : /* Logical output interface */
+               case 'j': /* Target */
+               case 'p': /* Net family protocol */
+               case 's': /* Source mac */
+               case 'd': /* Destination mac */
+               case 'c': /* Set counters */
+                       if (!OPT_COMMANDS)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "No command specified");
+                       if (command != 'A' && command != 'D' && command != 'I' && command != 'C')
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Command and option do not match");
+                       if (c == 'i') {
+                               ebt_check_option2(&flags, OPT_IN);
+                               if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_IIN;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+big_iface_length:
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Interface name length cannot exceed %d characters",
+                                                     IFNAMSIZ - 1);
+                               xtables_parse_interface(optarg, fw.in, fw.in_mask);
+                               break;
+                       } else if (c == 2) {
+                               ebt_check_option2(&flags, OPT_LOGICALIN);
+                               if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_ILOGICALIN;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+                               strcpy(fw.logical_in, optarg);
+                               if (parse_iface(fw.logical_in, "--logical-in"))
+                                       return -1;
+                               break;
+                       } else if (c == 'o') {
+                               ebt_check_option2(&flags, OPT_OUT);
+                               if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_IOUT;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+
+                               xtables_parse_interface(optarg, fw.out, fw.out_mask);
+                               break;
+                       } else if (c == 3) {
+                               ebt_check_option2(&flags, OPT_LOGICALOUT);
+                               if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_ILOGICALOUT;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+                               strcpy(fw.logical_out, optarg);
+                               if (parse_iface(fw.logical_out, "--logical-out"))
+                                       return -1;
+                               break;
+                       } else if (c == 'j') {
+                               ebt_check_option2(&flags, OPT_JUMP);
+                               fw.jumpto = parse_target(optarg);
+                               fw.target = command_jump(&fw, fw.jumpto);
+                               break;
+                       } else if (c == 's') {
+                               ebt_check_option2(&flags, OPT_SOURCE);
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_ISOURCE;
+
+                               if (ebt_get_mac_and_mask(optarg, fw.sourcemac, fw.sourcemsk))
+                                       xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
+                               fw.bitmask |= EBT_SOURCEMAC;
+                               break;
+                       } else if (c == 'd') {
+                               ebt_check_option2(&flags, OPT_DEST);
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       fw.invflags |= EBT_IDEST;
+
+                               if (ebt_get_mac_and_mask(optarg, fw.destmac, fw.destmsk))
+                                       xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
+                               fw.bitmask |= EBT_DESTMAC;
+                               break;
+                       } else if (c == 'c') {
+                               ebt_check_option2(&flags, OPT_COUNT);
+                               if (ebt_check_inverse2(optarg, argc, argv))
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Unexpected '!' after -c");
+                               if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Option -c needs 2 arguments");
+
+                               fw.counters.pcnt = strtoull(optarg, &buffer, 10);
+                               if (*buffer != '\0')
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Packet counter '%s' invalid",
+                                                     optarg);
+                               fw.counters.bcnt = strtoull(argv[optind], &buffer, 10);
+                               if (*buffer != '\0')
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Packet counter '%s' invalid",
+                                                     argv[optind]);
+                               optind++;
+                               break;
+                       }
+                       ebt_check_option2(&flags, OPT_PROTOCOL);
+                       if (ebt_check_inverse2(optarg, argc, argv))
+                               fw.invflags |= EBT_IPROTO;
+
+                       fw.bitmask &= ~((unsigned int)EBT_NOPROTO);
+                       i = strtol(optarg, &buffer, 16);
+                       if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Problem with the specified protocol");
+                       if (*buffer != '\0') {
+                               struct ethertypeent *ent;
+
+                               if (!strcasecmp(optarg, "LENGTH")) {
+                                       fw.bitmask |= EBT_802_3;
+                                       break;
+                               }
+                               ent = getethertypebyname(optarg);
+                               if (!ent)
+                                       xtables_error(PARAMETER_PROBLEM,
+                                                     "Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
+                               fw.ethproto = ent->e_ethertype;
+                       } else
+                               fw.ethproto = i;
+
+                       if (fw.ethproto < 0x0600)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Sorry, protocols have values above or equal to 0x0600");
+                       break;
+               case 4  : /* Lc */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Lc is not supported in daemon mode");
+#endif
+                       ebt_check_option2(&flags, LIST_C);
+                       if (command != 'L')
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Use --Lc with -L");
+                       flags |= LIST_C;
+                       break;
+               case 5  : /* Ln */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Ln is not supported in daemon mode");
+#endif
+                       ebt_check_option2(&flags, LIST_N);
+                       if (command != 'L')
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Use --Ln with -L");
+                       if (flags & LIST_X)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Lx is not compatible with --Ln");
+                       flags |= LIST_N;
+                       break;
+               case 6  : /* Lx */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Lx is not supported in daemon mode");
+#endif
+                       ebt_check_option2(&flags, LIST_X);
+                       if (command != 'L')
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Use --Lx with -L");
+                       if (flags & LIST_N)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Lx is not compatible with --Ln");
+                       flags |= LIST_X;
+                       break;
+               case 12 : /* Lmac2 */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "--Lmac2 is not supported in daemon mode");
+#endif
+                       ebt_check_option2(&flags, LIST_MAC2);
+                       if (command != 'L')
+                               xtables_error(PARAMETER_PROBLEM,
+                                              "Use --Lmac2 with -L");
+                       flags |= LIST_MAC2;
+                       break;
+               case 8 : /* atomic-commit */
+/*                     if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--atomic-commit is not supported in daemon mode");
+                       replace->command = c;
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not allowed");
+                       replace->flags |= OPT_COMMAND;
+                       if (!replace->filename)
+                               ebt_print_error2("No atomic file specified");*/
+                       /* Get the information from the file */
+                       /*ebt_get_table(replace, 0);*/
+                       /* We don't want the kernel giving us its counters,
+                        * they would overwrite the counters extracted from
+                        * the file */
+                       /*replace->num_counters = 0;*/
+                       /* Make sure the table will be written to the kernel */
+                       /*free(replace->filename);
+                       replace->filename = NULL;
+                       break;*/
+               /*case 7 :*/ /* atomic-init */
+               /*case 10:*/ /* atomic-save */
+               /*case 11:*/ /* init-table */
+               /*      if (exec_style == EXEC_STYLE_DAEMON) {
+                               if (c == 7) {
+                                       ebt_print_error2("--atomic-init is not supported in daemon mode");
+                               } else if (c == 10)
+                                       ebt_print_error2("--atomic-save is not supported in daemon mode");
+                               ebt_print_error2("--init-table is not supported in daemon mode");
+                       }
+                       replace->command = c;
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not allowed");
+                       if (c != 11 && !replace->filename)
+                               ebt_print_error2("No atomic file specified");
+                       replace->flags |= OPT_COMMAND;
+                       {
+                               char *tmp = replace->filename;*/
+
+                               /* Get the kernel table */
+                               /*replace->filename = NULL;
+                               ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
+                               replace->filename = tmp;
+                       }
+                       break;
+               case 9 :*/ /* atomic */
+                       /*if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--atomic is not supported in daemon mode");
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("--atomic has to come before the command");*/
+                       /* A possible memory leak here, but this is not
+                        * executed in daemon mode */
+                       /*replace->filename = (char *)malloc(strlen(optarg) + 1);
+                       strcpy(replace->filename, optarg);
+                       break;
+               case 13 : *//* concurrent */
+                       /*signal(SIGINT, sighandler);
+                       signal(SIGTERM, sighandler);
+                       use_lockfd = 1;
+                       break;*/
+               case 1 :
+                       if (!strcmp(optarg, "!"))
+                               ebt_check_inverse2(optarg, argc, argv);
+                       else
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Bad argument : '%s'", optarg);
+                       /* ebt_ebt_check_inverse2() did optind++ */
+                       optind--;
+                       continue;
+               default:
+                       /* Is it a target option? */
+                       /*t = (struct ebt_u_target *)new_entry->t;
+                       if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {
+                               if (ebt_errormsg[0] != '\0')
+                                       return -1;
+                               goto check_extension;
+                       }*/
+
+                       /* Is it a match_option? */
+                       /*for (m = ebt_matches; m; m = m->next)
+                               if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
+                                       break;
+
+                       if (m != NULL) {
+                               if (ebt_errormsg[0] != '\0')
+                                       return -1;
+                               if (m->used == 0) {
+                                       ebt_add_match(new_entry, m);
+                                       m->used = 1;
+                               }
+                               goto check_extension;
+                       }*/
+
+                       /* Is it a watcher option? */
+                       /*for (w = ebt_watchers; w; w = w->next)
+                               if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w))
+                                       break;
+
+                       if (w == NULL && c == '?')
+                               ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
+                       else if (w == NULL) {
+                               if (!strcmp(t->name, "standard"))
+                                       ebt_print_error2("Unknown argument: don't forget the -t option");
+                               else
+                                       ebt_print_error2("Target-specific option does not correspond with specified target");
+                       }
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       if (w->used == 0) {
+                               ebt_add_watcher(new_entry, w);
+                               w->used = 1;
+                       }
+check_extension: */
+                       if (command != 'A' && command != 'I' &&
+                           command != 'D' && command != 'C')
+                               xtables_error(PARAMETER_PROBLEM,
+                                             "Extensions only for -A, -I, -D and -C");
+               }
+               ebt_invert = 0;
+       }
+
+       /* Just in case we didn't catch an error */
+       /*if (ebt_errormsg[0] != '\0')
+               return -1;
+
+       if (!(table = ebt_find_table(replace->name)))
+               ebt_print_error2("Bad table name");*/
+
+       if (command == 'h' && !(flags & OPT_ZERO)) {
+               print_help(fw.target, fw.matches, *table);
+               if (exec_style == EXEC_STYLE_PRG)
+                       exit(0);
+       }
+
+       /* Do the final checks */
+       /*if (replace->command == 'A' || replace->command == 'I' ||
+          replace->command == 'D' || replace->command == 'C') {*/
+               /* This will put the hook_mask right for the chains */
+               /*ebt_check_for_loops(replace);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+               entries = ebt_to_chain(replace);
+               m_l = new_entry->m_list;
+               w_l = new_entry->w_list;
+               t = (struct ebt_u_target *)new_entry->t;
+               while (m_l) {
+                       m = (struct ebt_u_match *)(m_l->m);
+                       m->final_check(new_entry, m->m, replace->name,
+                          entries->hook_mask, 0);
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       m_l = m_l->next;
+               }
+               while (w_l) {
+                       w = (struct ebt_u_watcher *)(w_l->w);
+                       w->final_check(new_entry, w->w, replace->name,
+                          entries->hook_mask, 0);
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       w_l = w_l->next;
+               }
+               t->final_check(new_entry, t->t, replace->name,
+                  entries->hook_mask, 0);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       }*/
+       /* So, the extensions can work with the host endian.
+        * The kernel does not have to do this of course */
+       fw.ethproto = htons(fw.ethproto);
+
+       if (command == 'P') {
+               if (selected_chain < NF_BR_NUMHOOKS && strcmp(policy, "RETURN")==0)
+                       xtables_error(PARAMETER_PROBLEM,
+                                     "Policy RETURN only allowed for user defined chains");
+               ret = nft_chain_set(h, *table, chain, policy, NULL);
+               if (ret < 0)
+                       xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+       } else if (command == 'L') {
+               ret = list_rules(h, chain, *table, rule_nr,
+                                flags&OPT_VERBOSE,
+                                flags&OPT_NUMERIC,
+                                /*flags&OPT_EXPANDED*/0,
+                                flags&LIST_N);
+               if (!(flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
+                       exit(0);
+       }
+       if (flags & OPT_ZERO) {
+               selected_chain = zerochain;
+               ret = nft_chain_zero_counters(h, chain, *table);
+       } else if (command == 'F') {
+               ret = nft_rule_flush(h, chain, *table);
+       } else if (command == 'A') {
+               ret = append_entry(h, chain, *table, &fw, 0,
+                                  flags&OPT_VERBOSE, true);
+       } else if (command == 'I') {
+               ret = append_entry(h, chain, *table, &fw, rule_nr - 1,
+                                  flags&OPT_VERBOSE, false);
+       } else if (command == 'D') {
+               ret = delete_entry(h, chain, *table, &fw, rule_nr - 1,
+                                  rule_nr_end, flags&OPT_VERBOSE);
+       } /*else if (replace->command == 'C') {
+               ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       }*/
+       /* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+        * --init-table fall through */
+
+       /*if (ebt_errormsg[0] != '\0')
+               return -1;
+       if (table->check)
+               table->check(replace);
+
+       if (exec_style == EXEC_STYLE_PRG) {*//* Implies ebt_errormsg[0] == '\0' */
+               /*ebt_deliver_table(replace);
+
+               if (replace->nentries)
+                       ebt_deliver_counters(replace);*/
+       return ret;
+}
diff --git a/iptables/xtables-ebtables.h b/iptables/xtables-ebtables.h
new file mode 100644 (file)
index 0000000..1e479b1
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _XTABLES_EBTABLES_H_
+#define _XTABLES_EBTABLES_H_
+
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter/x_tables.h>
+
+/* We use replace->flags, so we can't use the following values:
+ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
+#define LIST_N    0x04
+#define LIST_C    0x08
+#define LIST_X    0x10
+#define LIST_MAC2 0x20
+
+/* Be backwards compatible, so don't use '+' in kernel */
+#define IF_WILDCARD 1
+
+extern unsigned char eb_mac_type_unicast[ETH_ALEN];
+extern unsigned char eb_msk_type_unicast[ETH_ALEN];
+extern unsigned char eb_mac_type_multicast[ETH_ALEN];
+extern unsigned char eb_msk_type_multicast[ETH_ALEN];
+extern unsigned char eb_mac_type_broadcast[ETH_ALEN];
+extern unsigned char eb_msk_type_broadcast[ETH_ALEN];
+extern unsigned char eb_mac_type_bridge_group[ETH_ALEN];
+extern unsigned char eb_msk_type_bridge_group[ETH_ALEN];
+
+int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);
+
+struct xtables_ebt_entry {
+       unsigned int bitmask;
+       unsigned int invflags;
+       unsigned int flags;
+       uint16_t ethproto;
+       char in[IFNAMSIZ];
+       char logical_in[IFNAMSIZ];
+       unsigned char in_mask[IFNAMSIZ];
+       char out[IFNAMSIZ];
+       char logical_out[IFNAMSIZ];
+       unsigned char out_mask[IFNAMSIZ];
+       unsigned char sourcemac[ETH_ALEN];
+       unsigned char sourcemsk[ETH_ALEN];
+       unsigned char destmac[ETH_ALEN];
+       unsigned char destmsk[ETH_ALEN];
+       struct xtables_rule_match *matches;
+       struct xtables_target *target;
+       struct xt_counters counters;
+       const char *jumpto;
+};
+#endif
index 5f487355bc145b1a3701bb449b4c8ea242ff14e4..30391e7fec7f0b176109d320dbcc820b8c6ce54d 100644 (file)
@@ -43,6 +43,7 @@ static const struct subcommand multi_subcommands[] = {
        {"xtables-config",      xtables_config_main},
        {"xtables-events",      xtables_events_main},
        {"xtables-arp",         xtables_arp_main},
+       {"xtables-ebtables",    xtables_eb_main},
 #endif
        {NULL},
 };
index e706894b98fccb1a5045e9bb9dbd97821d0c48c7..21e60b2b3463b10ac85d46400a53378158ab4ee2 100644 (file)
@@ -10,6 +10,7 @@ extern int xtables_ip6_main(int, char **);
 extern int xtables_ip6_save_main(int, char **);
 extern int xtables_ip6_restore_main(int, char **);
 extern int xtables_arp_main(int, char **);
+extern int xtables_eb_main(int, char **);
 extern int xtables_config_main(int, char **);
 extern int xtables_events_main(int, char **);
 #endif