summaryrefslogtreecommitdiff
path: root/ebtables.c
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
commit1abc55d3114378b4e73ec315eac6b122e55148c4 (patch)
tree2b4e1622873f7e6f8517b7862ec2b51a43a62d1a /ebtables.c
downloadebtables-1abc55d3114378b4e73ec315eac6b122e55148c4.tar.gz
Initial revision
Diffstat (limited to 'ebtables.c')
-rw-r--r--ebtables.c1655
1 files changed, 1655 insertions, 0 deletions
diff --git a/ebtables.c b/ebtables.c
new file mode 100644
index 0000000..e28fd96
--- /dev/null
+++ b/ebtables.c
@@ -0,0 +1,1655 @@
+/*
+ * ebtables.c, v2.0 April 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ * This code is 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h> // the database
+#include <netinet/in.h>
+#include <asm/types.h>
+#include "include/ebtables_u.h"
+
+// here are the number-name correspondences kept for the ethernet
+// frame type field
+#define PROTOCOLFILE "/etc/ethertypes"
+
+#define DATABASEHOOKNR NF_BR_NUMHOOKS
+#define DATABASEHOOKNAME "DB"
+
+static char *prog_name = PROGNAME;
+static char *prog_version = PROGVERSION;
+char* hooknames[NF_BR_NUMHOOKS] = {
+ [NF_BR_PRE_ROUTING]"PREROUTING",
+ [NF_BR_LOCAL_IN]"INPUT",
+ [NF_BR_FORWARD]"FORWARD",
+ [NF_BR_LOCAL_OUT]"OUTPUT",
+ [NF_BR_POST_ROUTING]"POSTROUTING",
+ [NF_BR_BROUTING]"BROUTING"
+};
+
+// default command line options
+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' },
+ { "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' },
+ { "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' },
+ { 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+// yup, all the possible target names
+char* standard_targets[NUM_STANDARD_TARGETS] = {
+ "ACCEPT",
+ "DROP",
+ "CONTINUE",
+};
+
+unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+
+// tells what happened to the old rules
+static unsigned short *counterchanges;
+// holds all the data
+static struct ebt_u_replace replace;
+
+// the chosen table
+static struct ebt_u_table *table = NULL;
+// the lists of supported tables, matches, watchers and targets
+static struct ebt_u_table *tables = NULL;
+static struct ebt_u_match *matches = NULL;
+static struct ebt_u_watcher *watchers = NULL;
+static struct ebt_u_target *targets = NULL;
+
+struct ebt_u_target *find_target(const char *name)
+{
+ struct ebt_u_target *t = targets;
+
+ while(t && strcmp(t->name, name))
+ t = t->next;
+ return t;
+}
+
+struct ebt_u_match *find_match(const char *name)
+{
+ struct ebt_u_match *m = matches;
+
+ while(m && strcmp(m->name, name))
+ m = m->next;
+ return m;
+}
+
+struct ebt_u_watcher *find_watcher(const char *name)
+{
+ struct ebt_u_watcher *w = watchers;
+
+ while(w && strcmp(w->name, name))
+ w = w->next;
+ return w;
+}
+
+struct ebt_u_table *find_table(char *name)
+{
+ struct ebt_u_table *t = tables;
+
+ while (t && strcmp(t->name, name))
+ t = t->next;
+ return t;
+}
+
+// The pointers in here are special:
+// The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
+// instead of making yet a few other structs, we just do a cast.
+// We need a struct ebt_u_target pointer because we know the address of the data
+// they point to won't change. We want to allow that the struct ebt_u_target.t
+// member can change.
+// Same holds for the struct ebt_match and struct ebt_watcher pointers
+struct ebt_u_entry *new_entry;
+
+void initialize_entry(struct ebt_u_entry *e)
+{
+ e->bitmask = EBT_NOPROTO;
+ e->invflags = 0;
+ e->ethproto = 0;
+ strcpy(e->in, "");
+ strcpy(e->out, "");
+ strcpy(e->logical_in, "");
+ strcpy(e->logical_out, "");
+ e->m_list = NULL;
+ e->w_list = NULL;
+ // the init function of the standard target should have put the verdict
+ // on CONTINUE
+ e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+ if (!e->t)
+ print_bug("Couldn't load standard target\n");
+}
+
+// this doesn't free e, becoz the calling function might need e->next
+void free_u_entry(struct ebt_u_entry *e)
+{
+ struct ebt_u_match_list *m_l, *m_l2;
+ struct ebt_u_watcher_list *w_l, *w_l2;
+
+ m_l = e->m_list;
+ while (m_l) {
+ m_l2 = m_l->next;
+ free(m_l->m);
+ free(m_l);
+ m_l = m_l2;
+ }
+ w_l = e->w_list;
+ while (w_l) {
+ w_l2 = w_l->next;
+ free(w_l->w);
+ free(w_l);
+ w_l = w_l2;
+ }
+ free(e->t);
+}
+
+// the user will use the match, so put it in new_entry
+static void add_match(struct ebt_u_match *m)
+{
+ struct ebt_u_match_list **m_list, *new;
+
+ m->used = 1;
+ for (m_list = &new_entry->m_list;
+ *m_list; m_list = &(*m_list)->next);
+ new = (struct ebt_u_match_list *)
+ malloc(sizeof(struct ebt_u_match_list));
+ if (!new)
+ print_memory();
+ *m_list = new;
+ new->next = NULL;
+ new->m = (struct ebt_entry_match *)m;
+}
+
+static void add_watcher(struct ebt_u_watcher *w)
+{
+ struct ebt_u_watcher_list **w_list;
+ struct ebt_u_watcher_list *new;
+
+ w->used = 1;
+ for (w_list = &new_entry->w_list;
+ *w_list; w_list = &(*w_list)->next);
+ new = (struct ebt_u_watcher_list *)
+ malloc(sizeof(struct ebt_u_watcher_list));
+ if (!new)
+ print_memory();
+ *w_list = new;
+ new->next = NULL;
+ new->w = (struct ebt_entry_watcher *)w;
+}
+
+static int global_option_offset = 0;
+#define OPTION_OFFSET 256
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *options_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ if (!newopts || !oldopts || !options_offset)
+ print_bug("merge wrong");
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ global_option_offset += OPTION_OFFSET;
+ *options_offset = global_option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ if (!merge)
+ print_memory();
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *options_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+ // only free dynamically allocated stuff
+ if (oldopts != ebt_original_options)
+ free(oldopts);
+
+ return merge;
+}
+
+void register_match(struct ebt_u_match *m)
+{
+ int size = m->size + sizeof(struct ebt_entry_match);
+ struct ebt_u_match **i;
+
+ m->m = (struct ebt_entry_match *)malloc(size);
+ if (!m->m)
+ print_memory();
+ strcpy(m->m->u.name, m->name);
+ m->m->match_size = m->size;
+ ebt_options = merge_options
+ (ebt_options, m->extra_ops, &(m->option_offset));
+ m->init(m->m);
+
+ for (i = &matches; *i; i = &((*i)->next));
+ m->next = NULL;
+ *i = m;
+}
+
+void register_watcher(struct ebt_u_watcher *w)
+{
+ int size = w->size + sizeof(struct ebt_entry_watcher);
+ struct ebt_u_watcher **i;
+
+ w->w = (struct ebt_entry_watcher *)malloc(size);
+ if (!w->w)
+ print_memory();
+ strcpy(w->w->u.name, w->name);
+ w->w->watcher_size = w->size;
+ ebt_options = merge_options
+ (ebt_options, w->extra_ops, &(w->option_offset));
+ w->init(w->w);
+
+ for (i = &watchers; *i; i = &((*i)->next));
+ w->next = NULL;
+ *i = w;
+}
+
+void register_target(struct ebt_u_target *t)
+{
+ int size = t->size + sizeof(struct ebt_entry_target);
+ struct ebt_u_target **i;
+
+ t->t = (struct ebt_entry_target *)malloc(size);
+ if (!t->t)
+ print_memory();
+ strcpy(t->t->u.name, t->name);
+ t->t->target_size = t->size;
+ ebt_options = merge_options
+ (ebt_options, t->extra_ops, &(t->option_offset));
+ t->init(t->t);
+ for (i = &targets; *i; i = &((*i)->next));
+ t->next = NULL;
+ *i = t;
+}
+
+void register_table(struct ebt_u_table *t)
+{
+ t->next = tables;
+ tables = t;
+}
+
+// used to parse /etc/etherproto
+int disregard_whitespace(char *buffer, FILE *ifp)
+{
+ int hlp;
+ buffer[0] = '\t';
+ while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+ hlp = fscanf(ifp, "%c", buffer);
+ if (hlp == EOF || hlp == 0) return -1;
+ }
+ return 0;
+}
+
+// used to parse /etc/etherproto
+int disregard_tabspace(char *buffer, FILE *ifp)
+{
+ int hlp;
+ buffer[0] = '\t';
+ while (buffer[0] == '\t' || buffer[0] == ' ') {
+ hlp = fscanf(ifp, "%c", buffer);
+ if (hlp == EOF || hlp == 0) return -1;
+ }
+ return 0;
+}
+
+// helper function: processes a line of data from the file /etc/etherproto
+int get_a_line(char *buffer, char *value, FILE *ifp)
+{
+ int i, hlp;
+ char anotherhlp;
+
+ /* discard comment lines && whitespace*/
+ while (1) {
+ if (disregard_whitespace(buffer, ifp)) return -1;
+ if (buffer[0] == '#')
+ while (1) {
+ hlp = fscanf(ifp, "%c", &anotherhlp);
+ if (!hlp || hlp == EOF)
+ return -1;
+ if (anotherhlp == '\n')
+ break;
+ }
+ else break;
+ }
+
+ // buffer[0] already contains the first letter
+ for (i = 1; i < 21; i++) {
+ hlp = fscanf(ifp, "%c", buffer + i);
+ if (hlp == EOF || hlp == 0) return -1;
+ if (buffer[i] == '\t' || buffer[i] == ' ')
+ break;
+ }
+ if (i == 21) return -1;
+ buffer[i] = '\0';
+ if (disregard_tabspace(value, ifp))
+ return -1;
+ // maybe I should allow 0x0800 instead of 0800, but I'm feeling lazy
+ // buffer[0] already contains the first letter
+ for (i = 1; i < 5; i++) {
+ hlp = fscanf(ifp, "%c", value+i);
+ if (value[i] == '\n' || value[i] == '\t' ||
+ value[i] == ' ' || hlp == EOF)
+ break;
+ }
+ if (i == 5) return -1;
+ /* discard comments at the end of a line */
+ if (value[i] == '\t' || value[i] == ' ')
+ while (1) {
+ hlp = fscanf(ifp, "%c", &anotherhlp);
+ if (!hlp || hlp == EOF || anotherhlp == '\n')
+ break;
+ }
+ value[i] = '\0';
+ return 0;
+}
+
+// helper function for list_em()
+int number_to_name(unsigned short proto, char *name)
+{
+ FILE *ifp;
+ char buffer[21], value[5], *bfr;
+ unsigned short i;
+
+ if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+ return -1;
+ while (1) {
+ if (get_a_line(buffer, value, ifp)) {
+ fclose(ifp);
+ return -1;
+ }
+ i = (unsigned short) strtol(value, &bfr, 16);
+ if (*bfr != '\0' || i != proto)
+ continue;
+ strcpy(name, buffer);
+ fclose(ifp);
+ return 0;
+ }
+}
+
+// helper function for list_rules()
+static void list_em(int hooknr)
+{
+ int i, j, space = 0, digits;
+ struct ebt_u_entry *hlp;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
+ struct ebt_u_match *m;
+ struct ebt_u_watcher *w;
+ struct ebt_u_target *t;
+ char name[21];
+
+ hlp = replace.hook_entry[hooknr]->entries;
+ printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
+ standard_targets[replace.hook_entry[hooknr]->policy]);
+ printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
+
+ i = replace.hook_entry[hooknr]->nentries;
+ while (i >9) {
+ space++;
+ i /= 10;
+ }
+
+ for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
+ digits = 0;
+ // A little work to get nice rule numbers.
+ while (j > 9) {
+ digits++;
+ j /= 10;
+ }
+ for (j = 0; j < space - digits; j++)
+ printf(" ");
+ printf("%d. ", i + 1);
+
+ // Don't print anything about the protocol if no protocol was
+ // specified, obviously this means any protocol will do.
+ if (!(hlp->bitmask & EBT_NOPROTO)) {
+ printf("eth proto: ");
+ if (hlp->invflags & EBT_IPROTO)
+ printf("! ");
+ if (hlp->bitmask & EBT_802_3)
+ printf("Length, ");
+ else {
+ if (number_to_name(ntohs(hlp->ethproto), name))
+ printf("0x%x, ", ntohs(hlp->ethproto));
+ else
+ printf("%s, ", name);
+ }
+ }
+ if (hlp->bitmask & EBT_SOURCEMAC) {
+ char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ printf("source mac: ");
+ if (hlp->invflags & EBT_ISOURCE)
+ printf("! ");
+ if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
+ !memcmp(hlp->sourcemsk, msk_type_unicast, 6)) {
+ printf("Unicast");
+ goto endsrc;
+ }
+ if (!memcmp(hlp->sourcemac, mac_type_multicast, 6) &&
+ !memcmp(hlp->sourcemsk, msk_type_multicast, 6)) {
+ printf("Multicast");
+ goto endsrc;
+ }
+ if (!memcmp(hlp->sourcemac, mac_type_broadcast, 6) &&
+ !memcmp(hlp->sourcemsk, msk_type_broadcast, 6)) {
+ printf("Broadcast");
+ goto endsrc;
+ }
+ for (j = 0; j < ETH_ALEN; j++)
+ printf("%02x%s", hlp->sourcemac[j],
+ (j == ETH_ALEN - 1) ? "" : ":");
+ if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
+ printf("/");
+ for (j = 0; j < ETH_ALEN; j++)
+ printf("%02x%s", hlp->sourcemsk[j],
+ (j == ETH_ALEN - 1) ? "" : ":");
+ }
+endsrc:
+ printf(", ");
+ }
+ if (hlp->bitmask & EBT_DESTMAC) {
+ char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ printf("dest mac: ");
+ if (hlp->invflags & EBT_IDEST)
+ printf("! ");
+ if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
+ !memcmp(hlp->destmsk, msk_type_unicast, 6)) {
+ printf("Unicast");
+ goto enddst;
+ }
+ if (!memcmp(hlp->destmac, mac_type_multicast, 6) &&
+ !memcmp(hlp->destmsk, msk_type_multicast, 6)) {
+ printf("Multicast");
+ goto enddst;
+ }
+ if (!memcmp(hlp->destmac, mac_type_broadcast, 6) &&
+ !memcmp(hlp->destmsk, msk_type_broadcast, 6)) {
+ printf("Broadcast");
+ goto enddst;
+ }
+ for (j = 0; j < ETH_ALEN; j++)
+ printf("%02x%s", hlp->destmac[j],
+ (j == ETH_ALEN - 1) ? "" : ":");
+ if (memcmp(hlp->destmsk, hlpmsk, 6)) {
+ printf("/");
+ for (j = 0; j < ETH_ALEN; j++)
+ printf("%02x%s", hlp->destmsk[j],
+ (j == ETH_ALEN - 1) ? "" : ":");
+ }
+enddst:
+ printf(", ");
+ }
+ if (hlp->in[0] != '\0') {
+ if (hlp->invflags & EBT_IIN)
+ printf("! ");
+ printf("in-if: %s, ", hlp->in);
+ }
+ if (hlp->logical_in[0] != '\0') {
+ if (hlp->invflags & EBT_ILOGICALIN)
+ printf("! ");
+ printf("logical in-if: %s, ", hlp->logical_in);
+ }
+ if (hlp->logical_out[0] != '\0') {
+ if (hlp->invflags & EBT_ILOGICALOUT)
+ printf("! ");
+ printf("logical out-if: %s, ", hlp->logical_out);
+ }
+ if (hlp->out[0] != '\0') {
+ if (hlp->invflags & EBT_IOUT)
+ printf("! ");
+ printf("out-if: %s, ", hlp->out);
+ }
+
+ m_l = hlp->m_list;
+ while (m_l) {
+ m = find_match(m_l->m->u.name);
+ if (!m)
+ 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 = find_watcher(w_l->w->u.name);
+ if (!w)
+ print_bug("Watcher not found");
+ w->print(hlp, w_l->w);
+ w_l = w_l->next;
+ }
+
+ printf("target: ");
+ t = find_target(hlp->t->u.name);
+ if (!t)
+ print_bug("Target not found");
+ t->print(hlp, hlp->t);
+ printf(", count = %llu",
+ replace.counters[replace.counter_entry[hooknr] + i].pcnt);
+ printf("\n");
+ hlp = hlp->next;
+ }
+}
+
+// parse the chain name and return the corresponding nr
+int get_hooknr(char* arg)
+{
+ int i;
+
+ // database is special case (not really a chain)
+ if (!strcmp(arg, DATABASEHOOKNAME))
+ return DATABASEHOOKNR;
+
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ if (!strcmp(arg, hooknames[i]))
+ return i;
+ return -1;
+}
+
+// yup, print out help
+void print_help()
+{
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
+
+ printf(
+"%s v%s\n"
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[b] [y,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"
+"--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"
+"--list -L "DATABASEHOOKNAME" : List the database (if present)\n"
+"--flush -F [chain] : Delete all rules in chain or in all chains\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"
+"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"
+"--version -V : print package version\n"
+"\n" ,
+ prog_name,
+ prog_version);
+
+ m_l = new_entry->m_list;
+ while (m_l) {
+ ((struct ebt_u_match *)m_l->m)->help();
+ printf("\n");
+ m_l = m_l->next;
+ }
+ w_l = new_entry->w_list;
+ while (w_l) {
+ ((struct ebt_u_watcher *)w_l->w)->help();
+ printf("\n");
+ w_l = w_l->next;
+ }
+ ((struct ebt_u_target *)new_entry->t)->help();
+ printf("\n");
+ if (table->help)
+ table->help(hooknames);
+ exit(0);
+}
+
+// execute command L
+static void list_rules()
+{
+ int i;
+
+ printf("Bridge table: %s\n", table->name);
+ if (replace.selected_hook != -1) list_em(replace.selected_hook);
+ else
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ if (replace.valid_hooks & (1 << i))
+ list_em(i);
+ return;
+}
+
+// execute command P
+static void change_policy(int policy)
+{
+ int i;
+
+ // don't do anything if the policy is the same
+ if (replace.hook_entry[replace.selected_hook]->policy != policy) {
+ replace.hook_entry[replace.selected_hook]->policy = policy;
+ replace.num_counters = replace.nentries;
+ if (replace.nentries) {
+ // '+ 1' for the CNT_END
+ if (!(counterchanges = (unsigned short *) malloc(
+ (replace.nentries + 1) * sizeof(unsigned short))))
+ print_memory();
+ // done nothing special to the rules
+ for (i = 0; i < replace.nentries; i++)
+ counterchanges[i] = CNT_NORM;
+ counterchanges[replace.nentries] = CNT_END;
+ }
+ else
+ counterchanges = NULL;
+ }
+ else
+ exit(0);
+}
+
+// flush one chain or the complete table
+static void flush_chains()
+{
+ int i, j, oldnentries;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *tmp;
+
+ // flush whole table
+ if (replace.selected_hook == -1) {
+ if (replace.nentries == 0)
+ exit(0);
+ replace.nentries = 0;
+ // no need for the kernel to give us counters back
+ replace.num_counters = 0;
+ // free everything and zero (n)entries
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (!(replace.valid_hooks & (1 << i)))
+ continue;
+ replace.hook_entry[i]->nentries = 0;
+ u_e = replace.hook_entry[i]->entries;
+ while (u_e) {
+ free_u_entry(u_e);
+ tmp = u_e->next;
+ free(u_e);
+ u_e = tmp;
+ }
+ replace.hook_entry[i]->entries = NULL;
+ }
+ return;
+ }
+
+ if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+ exit(0);
+ oldnentries = replace.nentries;
+ replace.nentries = replace.nentries -
+ replace.hook_entry[replace.selected_hook]->nentries;
+
+ // delete the counters belonging to the specified chain
+ if (replace.nentries) {
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)
+ malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (!(replace.valid_hooks & (1 << i)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ if (i != replace.selected_hook)
+ *cnt = CNT_NORM;
+ else
+ *cnt = CNT_DEL;
+ cnt++;
+ }
+ }
+ *cnt = CNT_END;
+ replace.num_counters = oldnentries;
+ }
+ else
+ replace.num_counters = 0;
+
+ replace.hook_entry[replace.selected_hook]->nentries = 0;
+ u_e = replace.hook_entry[replace.selected_hook]->entries;
+ while (u_e) {
+ free_u_entry(u_e);
+ tmp = u_e->next;
+ free(u_e);
+ u_e = tmp;
+ }
+ replace.hook_entry[replace.selected_hook]->entries = NULL;
+}
+
+// -1 == no match
+static int check_rule_exists(int rule_nr)
+{
+ struct ebt_u_entry *u_e;
+ struct ebt_u_match_list *m_l, *m_l2;
+ struct ebt_u_match *m;
+ struct ebt_u_watcher_list *w_l, *w_l2;
+ struct ebt_u_watcher *w;
+ struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
+ int i, j, k;
+
+ // handle '-D chain rulenr' command
+ if (rule_nr != -1) {
+ if (rule_nr >
+ replace.hook_entry[replace.selected_hook]->nentries)
+ return 0;
+ // user starts counting from 1
+ return rule_nr - 1;
+ }
+ u_e = replace.hook_entry[replace.selected_hook]->entries;
+ // check for an existing rule (if there are duplicate rules,
+ // take the first occurance)
+ for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
+ i++, u_e = u_e->next) {
+ if (!u_e)
+ print_bug("Hmm, trouble");
+ if ( u_e->ethproto == new_entry->ethproto
+ && !strcmp(u_e->in, new_entry->in)
+ && !strcmp(u_e->out, new_entry->out)
+ && u_e->bitmask == new_entry->bitmask) {
+ if (new_entry->bitmask & EBT_SOURCEMAC &&
+ strcmp(u_e->sourcemac, new_entry->sourcemac))
+ continue;
+ if (new_entry->bitmask & EBT_DESTMAC &&
+ strcmp(u_e->destmac, new_entry->destmac))
+ continue;
+ if (new_entry->bitmask != u_e->bitmask ||
+ new_entry->invflags != u_e->invflags)
+ continue;
+ // compare all matches
+ m_l = new_entry->m_list;
+ j = 0;
+ while (m_l) {
+ m = (struct ebt_u_match *)(m_l->m);
+ m_l2 = u_e->m_list;
+ while (m_l2 &&
+ strcmp(m_l2->m->u.name, m->m->u.name))
+ m_l2 = m_l2->next;
+ if (!m_l2 || !m->compare(m->m, m_l2->m))
+ goto letscontinue;
+ j++;
+ m_l = m_l->next;
+ }
+ // now be sure they have the same nr of matches
+ k = 0;
+ m_l = u_e->m_list;
+ while (m_l) {
+ k++;
+ m_l = m_l->next;
+ }
+ if (j != k)
+ continue;
+
+ // compare all watchers
+ w_l = new_entry->w_list;
+ j = 0;
+ while (w_l) {
+ w = (struct ebt_u_watcher *)(w_l->w);
+ w_l2 = u_e->w_list;
+ while (w_l2 &&
+ strcmp(w_l2->w->u.name, w->w->u.name))
+ w_l2 = w_l2->next;
+ if (!w_l2 || !w->compare(w->w, w_l2->w))
+ goto letscontinue;
+ j++;
+ w_l = w_l->next;
+ }
+ k = 0;
+ w_l = u_e->w_list;
+ while (w_l) {
+ k++;
+ w_l = w_l->next;
+ }
+ if (j != k)
+ continue;
+ if (strcmp(t->t->u.name, u_e->t->u.name))
+ continue;
+ if (!t->compare(t->t, u_e->t))
+ continue;
+ return i;
+ }
+letscontinue:
+ }
+ return -1;
+}
+
+// execute command A
+static void add_rule(int rule_nr)
+{
+ int i, j;
+ struct ebt_u_entry *u_e, *u_e2;
+ unsigned short *cnt;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
+
+ if (rule_nr != -1) { // command -I
+ if (--rule_nr >
+ replace.hook_entry[replace.selected_hook]->nentries)
+ print_error("rule nr too high: %d > %d", rule_nr,
+ replace.hook_entry[replace.selected_hook]->nentries);
+ } else
+ rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
+ // we're adding one rule
+ replace.num_counters = replace.nentries;
+ replace.nentries++;
+ replace.hook_entry[replace.selected_hook]->nentries++;
+
+ // handle counter stuff
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)
+ malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < replace.selected_hook; i++) {
+ if (!(replace.valid_hooks & (1 << i)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
+ for (i = 0; i < rule_nr; i++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_ADD;
+ cnt++;
+ while (cnt != counterchanges + replace.nentries) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+ u_e = replace.hook_entry[replace.selected_hook]->entries;
+ for (i = 0; i < rule_nr; i++) {
+ u_e2 = u_e;
+ u_e = u_e->next;
+ }
+ // insert the rule
+ if (u_e2)
+ u_e2->next = new_entry;
+ else
+ replace.hook_entry[replace.selected_hook]->entries = new_entry;
+ new_entry->next = u_e;
+
+ // put the ebt_[match, watcher, target] pointers in place
+ m_l = new_entry->m_list;
+ while (m_l) {
+ m_l->m = ((struct ebt_u_match *)m_l->m)->m;
+ m_l = m_l->next;
+ }
+ w_l = new_entry->w_list;
+ while (w_l) {
+ w_l->w = ((struct ebt_u_watcher *)w_l->w)->w;
+ w_l = w_l->next;
+ }
+ new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
+}
+
+// execute command D
+static void delete_rule(int rule_nr)
+{
+ int i, j, lentmp = 0;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *u_e2;
+
+ if ( (i = check_rule_exists(rule_nr)) == -1 )
+ print_error("Sorry, rule does not exists");
+
+ // we're deleting a rule
+ replace.num_counters = replace.nentries;
+ replace.nentries--;
+
+ if (replace.nentries) {
+ for (j = 0; j < replace.selected_hook; j++) {
+ if (!(replace.valid_hooks & (1 << j)))
+ continue;
+ lentmp += replace.hook_entry[j]->nentries;
+ }
+ lentmp += i;
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)malloc(
+ (replace.num_counters + 1) * sizeof(unsigned short))) )
+ print_memory();
+ cnt = counterchanges;
+ for (j = 0; j < lentmp; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_DEL;
+ cnt++;
+ for (j = 0; j < replace.num_counters - lentmp; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+ }
+ else
+ replace.num_counters = 0;
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+ u_e = replace.hook_entry[replace.selected_hook]->entries;
+ for (j = 0; j < i; j++) {
+ u_e2 = u_e;
+ u_e = u_e->next;
+ }
+
+ // remove from the chain
+ if (u_e2)
+ u_e2->next = u_e->next;
+ else
+ replace.hook_entry[replace.selected_hook]->entries = u_e->next;
+
+ replace.hook_entry[replace.selected_hook]->nentries--;
+ // free everything
+ free_u_entry(u_e);
+ free(u_e);
+}
+
+// execute command Z
+void zero_counters(int zerochain)
+{
+
+ if (zerochain == -1) {
+ // tell main() we don't update the counters
+ // this results in tricking the kernel to zero his counters,
+ // naively expecting userspace to update its counters. Muahahaha
+ counterchanges = NULL;
+ replace.num_counters = 0;
+ } else {
+ int i, j;
+ unsigned short *cnt;
+
+ if (replace.hook_entry[zerochain]->nentries == 0)
+ exit(0);
+ counterchanges = (unsigned short *)
+ malloc((replace.nentries + 1) * sizeof(unsigned short));
+ if (!counterchanges)
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < zerochain; i++) {
+ if (!(replace.valid_hooks & (1 << i)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
+ for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
+ *cnt = CNT_ZERO;
+ cnt++;
+ }
+ while (cnt != counterchanges + replace.nentries) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+ }
+}
+
+// list the database (optionally compiled into the kernel)
+static void list_db()
+{
+ struct brdb_dbinfo nr;
+ struct brdb_dbentry *db;
+ char name[21];
+ int i;
+
+ get_dbinfo(&nr);
+
+ // 0 : database disabled (-db n)
+ if (!(nr.nentries))
+ print_error("Database not present"
+ " (disabled), try ebtables --db y");
+ nr.nentries--;
+ if (!nr.nentries) print_error("Database empty");
+ if ( !(db = (struct brdb_dbentry *)
+ malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
+ print_memory();
+
+ get_db(nr.nentries, db);
+ printf("number of entries: %d\n", nr.nentries);
+ for (i = 0; i < nr.nentries; i++) {
+ printf(
+ "%d:\n"
+ "hook : %s\n"
+ "in-if : %s\n"
+ "out-if : %s\n"
+ "protocol: ", i + 1, hooknames[db->hook], db->in, db->out);
+ if (db->ethproto == IDENTIFY802_3)
+ printf("802.2/802.3 STYLE LENGTH FIELD\n");
+ else {
+ if (number_to_name(ntohs(db->ethproto), name))
+ printf("%x\n",ntohs(db->ethproto));
+ else
+ printf("%s\n", name);
+ }
+ db++;
+ }
+ exit(0);
+}
+
+// handle db [dis,en]abling
+static void allowdb(char yorn)
+{
+ __u16 decision;
+
+ if (yorn != 'y' && yorn != 'n')
+ print_error("Option [y] or [n] needed");
+
+ if (yorn == 'y')
+ decision = BRDB_DB;
+ else
+ decision = BRDB_NODB;
+
+ deliver_allowdb(&decision);
+
+ exit(0);
+}
+
+// set ethproto
+int name_to_protocol(char *name)
+{
+ FILE *ifp;
+ char buffer[21], value[5], *bfr;
+ unsigned short i;
+
+ if (!strcasecmp("LENGTH", name)) {
+ new_entry->ethproto = 0;
+ new_entry->bitmask |= EBT_802_3;
+ return 1;
+ }
+ if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+ return -1;
+ while (1) {
+ if (get_a_line(buffer, value, ifp)) return -1;
+ if (strcasecmp(buffer, name))
+ continue;
+ i = (unsigned short) strtol(value, &bfr, 16);
+ if (*bfr != '\0')
+ return -1;
+ new_entry->ethproto = i;
+ fclose(ifp);
+ return 0;
+ }
+ return -1;
+}
+
+// put the mac address into 6 (ETH_ALEN) bytes
+int getmac(char *from, char *to)
+{
+ int i, tmp;
+ char *buffer;
+
+ if (strlen(from) != 3 * ETH_ALEN - 1)
+ return -1;
+ for (i = 1; i < ETH_ALEN; i++) {
+ if (from[i*3 - 1] != ':')
+ return -1;
+ from[i*3 - 1] = '\0';
+ }
+ for (i = 0; i < ETH_ALEN; i++) {
+ tmp = strtol(from + i*3, &buffer, 16);
+ if (*buffer != '\0' || tmp > 255 || tmp < 0)
+ return -1;
+ to[i] = (unsigned char) tmp;
+ }
+ return 0;
+}
+
+int getmac_and_mask(char *from, char *to, char *mask)
+{
+ char *p;
+ int i;
+
+ if (strcasecmp(from, "Unicast") == 0) {
+ memcpy(to, mac_type_unicast, ETH_ALEN);
+ memcpy(mask, msk_type_unicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Multicast") == 0) {
+ memcpy(to, mac_type_multicast, ETH_ALEN);
+ memcpy(mask, msk_type_multicast, ETH_ALEN);
+ return 0;
+ }
+ if (strcasecmp(from, "Broadcast") == 0) {
+ memcpy(to, mac_type_broadcast, ETH_ALEN);
+ memcpy(mask, msk_type_broadcast, ETH_ALEN);
+ return 0;
+ }
+ if ( (p = strrchr(from, '/')) != NULL) {
+ *p = '\0';
+ if (getmac(p + 1, mask))
+ return -1;
+ } else
+ memset(mask, 0xff, ETH_ALEN);
+ if (getmac(from, to))
+ return -1;
+ for (i = 0; i < ETH_ALEN; i++)
+ to[i] &= mask[i];
+ return 0;
+}
+
+int check_inverse(const char option[])
+{
+ if (strcmp(option, "!") == 0) {
+ optind++;
+ return 1;
+ }
+ return 0;
+}
+
+void check_option(unsigned int *flags, unsigned int mask)
+{
+ if (*flags & mask)
+ print_error("Multiple use of same option not allowed");
+ *flags |= mask;
+}
+
+#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
+// the main thing
+int main(int argc, char *argv[])
+{
+ char *buffer, allowbc = 'n';
+ int c, i;
+ // this special one for the -Z option (we can have -Z <this> -L <that>)
+ int zerochain = -1;
+ int policy = -1;
+ int rule_nr = -1;// used for -D chain number
+ struct ebt_u_target *t;
+ struct ebt_u_match *m;
+ struct ebt_u_watcher *w;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
+
+ // initialize the table name, OPT_ flags, selected hook and command
+ strcpy(replace.name, "filter");
+ replace.flags = 0;
+ replace.selected_hook = -1;
+ replace.command = 'h';
+
+ new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+ if (!new_entry)
+ print_memory();
+ // put some sane values in our new entry
+ initialize_entry(new_entry);
+
+ // getopt saves the day
+ while ((c = getopt_long(argc, argv,
+ "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
+ switch (c) {
+
+ case 'A': // add a rule
+ case 'D': // delete a rule
+ case 'P': // define policy
+ case 'I': // insert a rule
+ replace.command = c;
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.flags |= OPT_COMMAND;
+ if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+ print_error("Bad chain");
+ if (c == 'D' && optind < argc &&
+ argv[optind][0] != '-') {
+ rule_nr = strtol(argv[optind], &buffer, 10);
+ if (*buffer != '\0' || rule_nr < 0)
+ print_error("Problem with the "
+ "specified rule number");
+ optind++;
+ }
+ if (c == 'P') {
+ if (optind >= argc)
+ print_error("No policy specified");
+ for (i = 0; i < 2; i++)
+ if (!strcmp(argv[optind],
+ standard_targets[i])) {
+ policy = i;
+ break;
+ }
+ if (policy == -1)
+ print_error("Wrong policy");
+ optind++;
+ }
+ if (c == 'I') {
+ if (optind >= argc)
+ print_error("No rulenr for -I"
+ " specified");
+ rule_nr = strtol(argv[optind], &buffer, 10);
+ if (*buffer != '\0' || rule_nr < 0)
+ print_error("Problem with the specified"
+ " rule number");
+ optind++;
+ }
+ break;
+
+ case 'L': // list
+ case 'F': // flush
+ case 'Z': // zero counters
+ if (c == 'Z') {
+ if (replace.flags & OPT_ZERO)
+ print_error("Multiple commands"
+ " not allowed");
+ if ( (replace.flags & OPT_COMMAND &&
+ replace.command != 'L'))
+ print_error("command -Z only allowed "
+ "together with command -L");
+ replace.flags |= OPT_ZERO;
+ } else {
+ replace.command = c;
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands"
+ " not allowed");
+ replace.flags |= OPT_COMMAND;
+ }
+ i = -1;
+ if (optarg) {
+ if ( (i = get_hooknr(optarg)) == -1 )
+ print_error("Bad chain");
+ } else
+ if (optind < argc && argv[optind][0] != '-') {
+ if ((i = get_hooknr(argv[optind]))
+ == -1)
+ print_error("Bad chain");
+ optind++;
+ }
+ if (i != -1) {
+ if (c == 'Z')
+ zerochain = i;
+ else
+ replace.selected_hook = i;
+ }
+ break;
+
+ case 'V': // version
+ replace.command = 'V';
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ printf("%s, %s\n", prog_name, prog_version);
+ exit(0);
+
+ case 'h': // help
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.command = 'h';
+ // All other arguments should be extension names
+ while (optind < argc) {
+ struct ebt_u_match *m;
+ struct ebt_u_watcher *w;
+
+ if ((m = find_match(argv[optind])))
+ add_match(m);
+ else if ((w = find_watcher(argv[optind])))
+ add_watcher(w);
+ else {
+ if (!(t = find_target(argv[optind])))
+ print_error("Extension %s "
+ "not found", argv[optind]);
+ if (replace.flags & OPT_JUMP)
+ print_error("Sorry, you can "
+ "only see help for one "
+ "target extension each time");
+ replace.flags |= OPT_JUMP;
+ new_entry->t =
+ (struct ebt_entry_target *)t;
+ }
+ optind++;
+ }
+ break;
+
+ case 't': // table
+ check_option(&replace.flags, OPT_TABLE);
+ if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
+ print_error("Table name too long");
+ strcpy(replace.name, 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
+ if ((replace.flags & OPT_COMMAND) == 0)
+ print_error("No command specified");
+ if ( replace.command != 'A' &&
+ replace.command != 'D' && replace.command != 'I')
+ print_error("Command and option do not match");
+ if (c == 'i') {
+ check_option(&replace.flags, OPT_IN);
+ if (replace.selected_hook > 2 &&
+ replace.selected_hook < NF_BR_BROUTING)
+ print_error("Use in-interface only in "
+ "INPUT, FORWARD, PREROUTING and"
+ "BROUTING chains");
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IIN;
+
+ if (optind > argc)
+ print_error("No in-interface "
+ "specified");
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interfacelength");
+ strcpy(new_entry->in, argv[optind - 1]);
+ break;
+ }
+ if (c == 2) {
+ check_option(&replace.flags, OPT_LOGICALIN);
+ if (replace.selected_hook > 2 &&
+ replace.selected_hook < NF_BR_BROUTING)
+ print_error("Use logical in-interface "
+ "only in INPUT, FORWARD, "
+ "PREROUTING and BROUTING chains");
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_ILOGICALIN;
+
+ if (optind > argc)
+ print_error("No logical in-interface "
+ "specified");
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interfacelength");
+ strcpy(new_entry->logical_in, argv[optind - 1]);
+ break;
+ }
+ if (c == 'o') {
+ check_option(&replace.flags, OPT_OUT);
+ if (replace.selected_hook < 2)
+ print_error("Use out-interface only"
+ " in OUTPUT, FORWARD and "
+ "POSTROUTING chains");
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IOUT;
+
+ if (optind > argc)
+ print_error("No out-interface "
+ "specified");
+
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interface "
+ "length");
+ strcpy(new_entry->out, argv[optind - 1]);
+ break;
+ }
+ if (c == 3) {
+ check_option(&replace.flags, OPT_LOGICALOUT);
+ if (replace.selected_hook < 2)
+ print_error("Use logical out-interface "
+ "only in OUTPUT, FORWARD and "
+ "POSTROUTING chains");
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_ILOGICALOUT;
+
+ if (optind > argc)
+ print_error("No logical out-interface "
+ "specified");
+
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interface "
+ "length");
+ strcpy(new_entry->logical_out,
+ argv[optind - 1]);
+ break;
+ }
+ if (c == 'j') {
+
+ check_option(&replace.flags, OPT_JUMP);
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(optarg,
+ standard_targets[i])) {
+ t = find_target(
+ EBT_STANDARD_TARGET);
+ ((struct ebt_standard_target *)
+ t->t)->verdict = i;
+ break;
+ }
+ // must be an extension then
+ if (i == NUM_STANDARD_TARGETS) {
+ struct ebt_u_target *t;
+ t = find_target(optarg);
+ // -j standard not allowed either
+ if (!t || t ==
+ (struct ebt_u_target *)new_entry->t)
+ print_error("Illegal target "
+ "name");
+ new_entry->t =
+ (struct ebt_entry_target *)t;
+ }
+ break;
+ }
+ if (c == 's') {
+ check_option(&replace.flags, OPT_SOURCE);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_ISOURCE;
+
+ if (optind > argc)
+ print_error("No source mac "
+ "specified");
+ if (getmac_and_mask(argv[optind - 1],
+ new_entry->sourcemac, new_entry->sourcemsk))
+ print_error("Problem with specified "
+ "source mac");
+ new_entry->bitmask |= EBT_SOURCEMAC;
+ break;
+ }
+ if (c == 'd') {
+ check_option(&replace.flags, OPT_DEST);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IDEST;
+
+ if (optind > argc)
+ print_error("No destination mac "
+ "specified");
+ if (getmac_and_mask(argv[optind - 1],
+ new_entry->destmac, new_entry->destmsk))
+ print_error("Problem with specified "
+ "destination mac");
+ new_entry->bitmask |= EBT_DESTMAC;
+ break;
+ }
+ check_option(&replace.flags, OPT_PROTOCOL);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IPROTO;
+
+ if (optind > argc)
+ print_error("No protocol specified");
+ new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+ i = strtol(argv[optind - 1], &buffer, 16);
+ if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+ print_error("Problem with the specified "
+ "protocol");
+ new_entry->ethproto = i;
+ if (*buffer != '\0')
+ if (name_to_protocol(argv[optind - 1]) == -1)
+ print_error("Problem with the specified"
+ " protocol");
+ if (new_entry->ethproto < 1536 &&
+ !(new_entry->bitmask & EBT_802_3))
+ print_error("Sorry, protocols have values above"
+ " or equal to 1536 (0x0600)");
+ break;
+
+ case 'b': // allow database?
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.command = c;
+ allowbc = *optarg;
+ break;
+
+ 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)))
+ continue;
+
+ // is it a match_option?
+ for (m = 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 (m->used == 0)
+ add_match(m);
+ continue;
+ }
+
+ // is it a watcher option?
+ for (w = watchers; w; w = w->next)
+ if (w->parse(c-w->option_offset, argv,
+ argc, new_entry, &w->flags, &w->w))
+ break;
+
+ if (w == NULL)
+ print_error("Unknown argument");
+ if (w->used == 0)
+ add_watcher(w);
+ }
+ }
+
+ // database stuff before ebtables stuff
+ if (replace.command == 'b')
+ allowdb(allowbc);
+ if (replace.command == 'L' && replace.selected_hook == DATABASEHOOKNR)
+ list_db();
+
+ if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
+ replace.flags & OPT_ZERO )
+ print_error("Command -Z only allowed together with command -L");
+
+ if (replace.command == 'A' || replace.command == 'I' ||
+ replace.command == 'D') {
+ if (replace.selected_hook == -1)
+ print_error("Not enough information");
+ }
+
+ if ( !(table = find_table(replace.name)) )
+ print_error("Bad table name");
+
+ // do this after parsing everything, so we can print specific info
+ if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ print_help();
+
+ // do the final checks
+ 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,
+ replace.selected_hook);
+ 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,
+ replace.selected_hook);
+ w_l = w_l->next;
+ }
+ t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+
+ // so, the extensions can work with the host endian
+ // the kernel does not have to do this ofcourse
+ new_entry->ethproto = htons(new_entry->ethproto);
+
+ // get the kernel's information
+ get_table(&replace);
+ // check if selected_hook is a valid_hook
+ if (replace.selected_hook >= 0 &&
+ !(replace.valid_hooks & (1 << replace.selected_hook)))
+ print_error("Bad chain name");
+ if (replace.command == 'P')
+ change_policy(policy);
+ else if (replace.command == 'L') {
+ list_rules();
+ if (replace.flags & OPT_ZERO)
+ zero_counters(zerochain);
+ else
+ exit(0);
+ }
+ if (replace.flags & OPT_ZERO)
+ zero_counters(zerochain);
+ else if (replace.command == 'F')
+ flush_chains();
+ else if (replace.command == 'A' || replace.command == 'I')
+ add_rule(rule_nr);
+ else if (replace.command == 'D')
+ delete_rule(rule_nr);
+
+ if (table->check)
+ table->check(&replace);
+
+ deliver_table(&replace);
+
+ if (counterchanges)
+ deliver_counters(&replace, counterchanges);
+ return 0;
+}