ebtables-v2.0.10-4/0000700000175000017500000000000011672451150014110 5ustar bdschuymbdschuymebtables-v2.0.10-4/ChangeLog0000644000175000017500000001716411672451147015713 0ustar bdschuymbdschuym20111215 Changelog for v2.0.10-4 * really fix counter setting bug (thanks to James' persistence) 20111204 Changelog for v2.0.10-3 * fix counter setting bug (reported by James Sinclair) 20110710 Changelog for v2.0.10-2 * enable compiler optimizations (-O3) * small changes to remove the compiler warnings due to optimization being turned on (thanks to Peter Volkov) * respect LDFLAGS in Makefiles (Peter Volkov) 20110710 Changelog for v2.0.10-1 * fix --among-dst-file, which translated to --among-src (reported by Thierry Watelet) * fix bug in test_ulog.c example * Makefile: respect LDFLAGS during ebtables build (Peter Volkov) * Makefile: create directories to avoid build failure when DESTDIR is supplied (Peter Volkov) * incorporate fixes for possible issues found by Coverity analysis (thanks to Jiri Popelka) * define __EXPORTED_HEADERS__ to get access to the Linux kernel headers * extend ebt_ip6 to allow matching on ipv6-icmp types/codes (by Florian Westphal) * Print a more useful error message when an update of the kernel table failed. * Add --concurrent option, which enables using a file lock to support concurrent scripts updating the ebtables kernel tables 20100203 Changelog for v2.0.9-2 * fix unwanted zeroing of counters in the last user-defined chain (reported by Jon Lewis) * fix hidden symbol compilation error when using ld directly * fix return value checking of creat to give a correct error message if the atomic file couldn't be created * correct info in INSTALL about compilation of ulog 20090621 Changelog for v2.0.9 vs v2.0.8-2 * added ip6 module for filtering IPv6 traffic (Kuo-Lang Tseng, Manohar Castelino) * added --log-ip6 option for logging IPv6 traffic (Kuo-Lang Tseng, Manohar Castelino) * added nflog watcher for logging packets to userspace (Peter Warasin) * bugfix in ebtables.sysv (Michal Soltys) * bugfix for among match on x86-64 (reported by Pavel Emelyanov) 20061217 Since last entry: * fixed a few reported bugs * ebt_among --among-dst-file and --among-src-file: allow the list to be given in a file (circumvents command line max. line length * ebt_nat --snat-arp: if it's an arp packet, also change the source address in the arp header * ebt_mark --mark-or, --mark-xor, --mark-and 20051020 Since last entry: * ebtables modules are now located in /usr/lib/ebtables/ * added '/sbin/service ebtables' support * added ebtables-save (thanks to Rok Papez ) and ebtables-restore (the first one a perl script, the second one written in c (fast)) * optimized the code for the '-A' command, making ebtables-restore very fast. * ebtablesd/ebtablesu is deprecated and not compiled by default the ebtables-save/ebtables-restore scheme is much better 20050117 Since last entry: * added ulog watcher * made the ebtables code modular (make library functions). * added the ebtablesd/ebtablesu scheme to allow faster addition of rules (and to test the modular code). * some small fixes * added -c option (initialize counters) * added -C option (change counters) 20031102 Since last entry: * added arpreply and among modules * added limit match 20030724 * added (automatic) Sparc64 support, thanks to Michael Bellion and Thomas Heinz from hipac.org for providing a test-box. 20030717 * added stp frames match type 20030713 * added support for deleting all user-defined chains (-X option without specified chain) 20030601 * added --Lmac2 * Chris Vitale: basic 802.3/802.2 filtering (experimental, kernel files are in the CVS) 20030503 * added negative rule counter support * bugfix: bcnt was not updated correctly * Cedric Blancher: add ARP MAC matching support * added pkttype match 20030402 * fixed check bug in ebt_ip.c (report from joe_judge_at_guardium.com). 20030111 * fixed problem when removing a chain (report from ykphuah_at_greenpacket.com). * Added --help list_extensions which, well, lists the extensions 20021203 * changed the way to use the atomic operations. It's now possible to use the EBTABLES_ATOMIC_FILE environment variable, so it's no longer necessary to explicitly state the file name. See the man. 20021120 * changed the way of compiling. New releases will now contain their own set of kernel includes. No more copying of kernel includes to /usr/include/linux * added getethertype.c (Nick) and use it. Removed name_to_number() and number_to_name(). 20021106 * added possibility to specify a rule number interval when deleting rules 20021102 * added ! - option possibility, which is equivalent to - ! option 20021102 * since last entry: added byte counters and udp/tcp port matching 20020830 * updated the kernel files for 2.4.20-pre5 and 2.5.32 * last big cleanup of kernel and userspace code just finished 20020820 * ARP module bugfix * IP module bugfix * nat module bugfix 20020730 * other things done before 2.0-rc1 that I can think of, including kernel: * cache align counters for better smp performance * simplify snat code * check for --xxxx-target RETURN on base chain * cleanup code * minor bugfixes 20020724 * code cleanup * bugfix for --atomic-commit 20020720 * added mark target+match 20020714 * added --atomic options 20020710 * some unlogged changes (due to lazyness) * added --Lc, --Ln, --Lx 20020625 * user defined chains support: added -N, -X, -E options. 20020621 * some unlogged changes (due to lazyness) * change the output for -L to make it look like it would look when the user inputs the command. * try to autoload modules * some minor bugfixes * add user defined chains support (without new commands yet, deliberately) * comparing rules didn't take the logical devices into account 20020520 * update help for -s and -d * add VLAN in ethertypes * add SYMLINK option for compiling 20020501 * allow -i and --logical-in in BROUTING * update the manual page * rename /etc/etherproto into /etc/ethertypes (seems to be a more standard name) * add MAC mask for -s and -d, also added Unicast, Multicast and Broadcast specification for specifying a (family of) MAC addresses. 20020427 * added broute table. * added redirect target. * added --redirect-target, --snat-target and --dnat-target options. * added logical_out and logical_in * snat bugfix (->size) 20020414 * fixed some things in the manual. * fixed -P problem. 20020411 * -j standard no longer works, is this cryptic? good :) * lots of beautification. - made some code smaller - made everything fit within 80 columns * fix problems with -i and -o option * print_memory now prints useful info * trying to see the tables when ebtables is not loaded in kernel no longer makes this be seen as a bug. 20020403 ebtables v2.0 released, changes: * A complete rewrite, made everything modular. * Fixed a one year old bug in br_db.c. A similar bug was present in ebtables.c. It was visible when the number of rules got bigger (around 90). * Removed the option to allow/disallow counters. Frames passing by are always counted now. * Didn't really add any new functionality. However, it will be _alot_ easier and prettier to do so now. Feel free to add an extension yourself. * There are 4 types of extensions: - Tables. - Matches: like iptables has. - Watchers: these only watch frames that passed all the matches of the rule. They don't change the frame, nor give a verdict. The log extension is a watcher. - Targets. * user32/kernel64 architectures like the Sparc64 are unsupported. If you want me to change this, give me access to such a box, and don't pressure me. ebtables-v2.0.10-4/useful_functions.c0000644000175000017500000002520311672451147017671 0ustar bdschuymbdschuym/* * useful_functions.c, January 2004 * * Random collection of functions that can be used by extensions. * * 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 "include/ebtables_u.h" #include "include/ethernetdb.h" #include #include #include #include #include #include #include #include #include const unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0}; const unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0}; const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0}; const unsigned char msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255}; /* 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; 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)); } void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask) { char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (!memcmp(mac, mac_type_unicast, 6) && !memcmp(mask, msk_type_unicast, 6)) printf("Unicast"); else if (!memcmp(mac, mac_type_multicast, 6) && !memcmp(mask, msk_type_multicast, 6)) printf("Multicast"); else if (!memcmp(mac, mac_type_broadcast, 6) && !memcmp(mask, msk_type_broadcast, 6)) printf("Broadcast"); else if (!memcmp(mac, mac_type_bridge_group, 6) && !memcmp(mask, msk_type_bridge_group, 6)) printf("BGA"); else { ebt_print_mac(mac); if (memcmp(mask, hlpmsk, 6)) { printf("/"); ebt_print_mac(mask); } } } /* Checks the type for validity and calls getethertypebynumber(). */ struct ethertypeent *parseethertypebynumber(int type) { if (type < 1536) ebt_print_error("Ethernet protocols have values >= 0x0600"); if (type > 0xffff) ebt_print_error("Ethernet protocols have values <= 0xffff"); return getethertypebynumber(type); } /* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */ int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask) { char *p; int i; struct ether_addr *addr; 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 (strcasecmp(from, "BGA") == 0) { memcpy(to, mac_type_bridge_group, ETH_ALEN); memcpy(mask, 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; } /* 0: default * 1: the inverse '!' of the option has already been specified */ int ebt_invert = 0; /* * Check if the inverse of the option is specified. This is used * in the parse functions of the extensions and ebtables.c */ int _ebt_check_inverse(const char option[], int argc, char **argv) { if (!option) return ebt_invert; if (strcmp(option, "!") == 0) { if (ebt_invert == 1) ebt_print_error("Double use of '!' not allowed"); if (optind >= argc) optarg = NULL; else optarg = argv[optind]; optind++; ebt_invert = 1; return 1; } return ebt_invert; } /* Make sure the same option wasn't specified twice. This is used * in the parse functions of the extensions and ebtables.c */ void ebt_check_option(unsigned int *flags, unsigned int mask) { if (*flags & mask) ebt_print_error("Multiple use of same option not allowed"); *flags |= mask; } /* Put the ip string into 4 bytes. */ static int undot_ip(char *ip, unsigned char *ip2) { char *p, *q, *end; long int onebyte; int i; char buf[20]; strncpy(buf, ip, sizeof(buf) - 1); p = buf; for (i = 0; i < 3; i++) { if ((q = strchr(p, '.')) == NULL) return -1; *q = '\0'; onebyte = strtol(p, &end, 10); if (*end != '\0' || onebyte > 255 || onebyte < 0) return -1; ip2[i] = (unsigned char)onebyte; p = q + 1; } onebyte = strtol(p, &end, 10); if (*end != '\0' || onebyte > 255 || onebyte < 0) return -1; ip2[3] = (unsigned char)onebyte; return 0; } /* Put the mask into 4 bytes. */ static int ip_mask(char *mask, unsigned char *mask2) { char *end; long int bits; uint32_t mask22; if (undot_ip(mask, mask2)) { /* not the /a.b.c.e format, maybe the /x format */ bits = strtol(mask, &end, 10); if (*end != '\0' || bits > 32 || bits < 0) return -1; if (bits != 0) { mask22 = htonl(0xFFFFFFFF << (32 - bits)); memcpy(mask2, &mask22, 4); } else { mask22 = 0xFFFFFFFF; memcpy(mask2, &mask22, 4); } } return 0; } /* Set the ip mask and ip address. Callers should check ebt_errormsg[0]. * The string pointed to by address can be altered. */ void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk) { char *p; /* first the mask */ if ((p = strrchr(address, '/')) != NULL) { *p = '\0'; if (ip_mask(p + 1, (unsigned char *)msk)) { ebt_print_error("Problem with the IP mask '%s'", p + 1); return; } } else *msk = 0xFFFFFFFF; if (undot_ip(address, (unsigned char *)addr)) { ebt_print_error("Problem with the IP address '%s'", address); return; } *addr = *addr & *msk; } /* Transform the ip mask into a string ready for output. */ char *ebt_mask_to_dotted(uint32_t mask) { int i; static char buf[20]; uint32_t maskaddr, bits; maskaddr = ntohl(mask); /* don't print /32 */ if (mask == 0xFFFFFFFFL) { *buf = '\0'; return buf; } i = 32; bits = 0xFFFFFFFEL; /* Case 0xFFFFFFFF has just been dealt with */ while (--i >= 0 && maskaddr != bits) bits <<= 1; if (i > 0) sprintf(buf, "/%d", i); else if (!i) *buf = '\0'; else /* Mask was not a decent combination of 1's and 0's */ sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2], ((unsigned char *)&mask)[3]); return buf; } /* Most of the following code is derived from iptables */ static void in6addrcpy(struct in6_addr *dst, struct in6_addr *src) { memcpy(dst, src, sizeof(struct in6_addr)); } int string_to_number_ll(const char *s, unsigned long long min, unsigned long long max, unsigned long long *ret) { unsigned long long number; char *end; /* Handle hex, octal, etc. */ errno = 0; number = strtoull(s, &end, 0); if (*end == '\0' && end != s) { /* we parsed a number, let's see if we want this */ if (errno != ERANGE && min <= number && (!max || number <= max)) { *ret = number; return 0; } } return -1; } int string_to_number_l(const char *s, unsigned long min, unsigned long max, unsigned long *ret) { int result; unsigned long long number; result = string_to_number_ll(s, min, max, &number); *ret = (unsigned long)number; return result; } int string_to_number(const char *s, unsigned int min, unsigned int max, unsigned int *ret) { int result; unsigned long number; result = string_to_number_l(s, min, max, &number); *ret = (unsigned int)number; return result; } static struct in6_addr *numeric_to_addr(const char *num) { static struct in6_addr ap; int err; if ((err=inet_pton(AF_INET6, num, &ap)) == 1) return ≈ return (struct in6_addr *)NULL; } static struct in6_addr *parse_ip6_mask(char *mask) { static struct in6_addr maskaddr; struct in6_addr *addrp; unsigned int bits; if (mask == NULL) { /* no mask at all defaults to 128 bits */ memset(&maskaddr, 0xff, sizeof maskaddr); return &maskaddr; } if ((addrp = numeric_to_addr(mask)) != NULL) return addrp; if (string_to_number(mask, 0, 128, &bits) == -1) ebt_print_error("Invalid IPv6 Mask '%s' specified", mask); if (bits != 0) { char *p = (char *)&maskaddr; memset(p, 0xff, bits / 8); memset(p + (bits / 8) + 1, 0, (128 - bits) / 8); p[bits / 8] = 0xff << (8 - (bits & 7)); return &maskaddr; } memset(&maskaddr, 0, sizeof maskaddr); return &maskaddr; } /* Set the ipv6 mask and address. Callers should check ebt_errormsg[0]. * The string pointed to by address can be altered. */ void ebt_parse_ip6_address(char *address, struct in6_addr *addr, struct in6_addr *msk) { struct in6_addr *tmp_addr; char buf[256]; char *p; int i; int err; strncpy(buf, address, sizeof(buf) - 1); /* first the mask */ buf[sizeof(buf) - 1] = '\0'; if ((p = strrchr(buf, '/')) != NULL) { *p = '\0'; tmp_addr = parse_ip6_mask(p + 1); } else tmp_addr = parse_ip6_mask(NULL); in6addrcpy(msk, tmp_addr); /* if a null mask is given, the name is ignored, like in "any/0" */ if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any))) strcpy(buf, "::"); if ((err=inet_pton(AF_INET6, buf, addr)) < 1) { ebt_print_error("Invalid IPv6 Address '%s' specified", buf); return; } for (i = 0; i < 4; i++) addr->s6_addr32[i] &= msk->s6_addr32[i]; } /* Transform the ip6 addr into a string ready for output. */ char *ebt_ip6_to_numeric(const struct in6_addr *addrp) { /* 0000:0000:0000:0000:0000:000.000.000.000 * 0000:0000:0000:0000:0000:0000:0000:0000 */ static char buf[50+1]; return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); } ebtables-v2.0.10-4/libebtc.c0000644000175000017500000010566411672451147015714 0ustar bdschuymbdschuym/* * libebtc.c, January 2004 * * Contains the functions with which to make a table in userspace. * * 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 #include #include #include #include "include/ebtables_u.h" #include "include/ethernetdb.h" #include #include #include #include #include #include static void decrease_chain_jumps(struct ebt_u_replace *replace); static int iterate_entries(struct ebt_u_replace *replace, int type); /* The standard names */ const char *ebt_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" }; /* The four target names */ const char* ebt_standard_targets[NUM_STANDARD_TARGETS] = { "ACCEPT", "DROP", "CONTINUE", "RETURN", }; /* The lists of supported tables, matches, watchers and targets */ struct ebt_u_table *ebt_tables; struct ebt_u_match *ebt_matches; struct ebt_u_watcher *ebt_watchers; struct ebt_u_target *ebt_targets; /* Find the right structure belonging to a name */ struct ebt_u_target *ebt_find_target(const char *name) { struct ebt_u_target *t = ebt_targets; while (t && strcmp(t->name, name)) t = t->next; return t; } struct ebt_u_match *ebt_find_match(const char *name) { struct ebt_u_match *m = ebt_matches; while (m && strcmp(m->name, name)) m = m->next; return m; } struct ebt_u_watcher *ebt_find_watcher(const char *name) { struct ebt_u_watcher *w = ebt_watchers; while (w && strcmp(w->name, name)) w = w->next; return w; } struct ebt_u_table *ebt_find_table(const char *name) { struct ebt_u_table *t = ebt_tables; while (t && strcmp(t->name, name)) t = t->next; return t; } /* Prints all registered extensions */ void ebt_list_extensions() { struct ebt_u_table *tbl = ebt_tables; struct ebt_u_target *t = ebt_targets; struct ebt_u_match *m = ebt_matches; struct ebt_u_watcher *w = ebt_watchers; PRINT_VERSION; printf("Loaded userspace extensions:\n\nLoaded tables:\n"); while (tbl) { printf("%s\n", tbl->name); tbl = tbl->next; } printf("\nLoaded targets:\n"); while (t) { printf("%s\n", t->name); t = t->next; } printf("\nLoaded matches:\n"); while (m) { printf("%s\n", m->name); m = m->next; } printf("\nLoaded watchers:\n"); while (w) { printf("%s\n", w->name); w = w->next; } } #ifndef LOCKFILE #define LOCKDIR "/var/lib/ebtables" #define LOCKFILE LOCKDIR"/lock" #endif static int lockfd = -1, locked; int use_lockfd; /* Returns 0 on success, -1 when the file is locked by another process * or -2 on any other error. */ static int lock_file() { int try = 0; int ret = 0; sigset_t sigset; tryagain: /* the SIGINT handler will call unlock_file. To make sure the state * of the variable locked is correct, we need to temporarily mask the * SIGINT interrupt. */ sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigprocmask(SIG_BLOCK, &sigset, NULL); lockfd = open(LOCKFILE, O_CREAT | O_EXCL | O_WRONLY, 00600); if (lockfd < 0) { if (errno == EEXIST) ret = -1; else if (try == 1) ret = -2; else { if (mkdir(LOCKDIR, 00700)) ret = -2; else { try = 1; goto tryagain; } } } else { close(lockfd); locked = 1; } sigprocmask(SIG_UNBLOCK, &sigset, NULL); return ret; } void unlock_file() { if (locked) { remove(LOCKFILE); locked = 0; } } void __attribute__ ((destructor)) onexit() { if (use_lockfd) unlock_file(); } /* Get the table from the kernel or from a binary file * init: 1 = ask the kernel for the initial contents of a table, i.e. the * way it looks when the table is insmod'ed * 0 = get the current data in the table */ int ebt_get_kernel_table(struct ebt_u_replace *replace, int init) { int ret; if (!ebt_find_table(replace->name)) { ebt_print_error("Bad table name '%s'", replace->name); return -1; } while (use_lockfd && (ret = lock_file())) { if (ret == -2) { /* if we get an error we can't handle, we exit. This * doesn't break backwards compatibility since using * this file locking is disabled by default. */ ebt_print_error2("Unable to create lock file "LOCKFILE); } fprintf(stderr, "Trying to obtain lock %s\n", LOCKFILE); sleep(1); } /* Get the kernel's information */ if (ebt_get_table(replace, init)) { if (ebt_errormsg[0] != '\0') return -1; ebtables_insmod("ebtables"); if (ebt_get_table(replace, init)) { ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name); return -1; } } return 0; } /* Put sane values into a new entry */ void ebt_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; e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET); ebt_find_target(EBT_STANDARD_TARGET)->used = 1; e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0; if (!e->t) ebt_print_bug("Couldn't load standard target"); ((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE; } /* Free up the memory of the table held in userspace, *replace can be reused */ void ebt_cleanup_replace(struct ebt_u_replace *replace) { int i; struct ebt_u_entries *entries; struct ebt_cntchanges *cc1, *cc2; struct ebt_u_entry *u_e1, *u_e2; replace->name[0] = '\0'; replace->valid_hooks = 0; replace->nentries = 0; replace->num_counters = 0; replace->flags = 0; replace->command = 0; replace->selected_chain = -1; free(replace->filename); replace->filename = NULL; free(replace->counters); replace->counters = NULL; for (i = 0; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; u_e1 = entries->entries->next; while (u_e1 != entries->entries) { ebt_free_u_entry(u_e1); u_e2 = u_e1->next; free(u_e1); u_e1 = u_e2; } free(entries->entries); free(entries); replace->chains[i] = NULL; } cc1 = replace->cc->next; while (cc1 != replace->cc) { cc2 = cc1->next; free(cc1); cc1 = cc2; } replace->cc->next = replace->cc->prev = replace->cc; } /* Should be called, e.g., between 2 rule adds */ void ebt_reinit_extensions() { struct ebt_u_match *m; struct ebt_u_watcher *w; struct ebt_u_target *t; int size; /* The init functions should determine by themselves whether they are * called for the first time or not (when necessary). */ for (m = ebt_matches; m; m = m->next) { if (m->used) { size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); m->m = (struct ebt_entry_match *)malloc(size); if (!m->m) ebt_print_memory(); strcpy(m->m->u.name, m->name); m->m->match_size = EBT_ALIGN(m->size); m->used = 0; } m->flags = 0; /* An error can occur before used is set, while flags is changed. */ m->init(m->m); } for (w = ebt_watchers; w; w = w->next) { if (w->used) { size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); w->w = (struct ebt_entry_watcher *)malloc(size); if (!w->w) ebt_print_memory(); strcpy(w->w->u.name, w->name); w->w->watcher_size = EBT_ALIGN(w->size); w->used = 0; } w->flags = 0; w->init(w->w); } for (t = ebt_targets; t; t = t->next) { if (t->used) { size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); t->t = (struct ebt_entry_target *)malloc(size); if (!t->t) ebt_print_memory(); strcpy(t->t->u.name, t->name); t->t->target_size = EBT_ALIGN(t->size); t->used = 0; } t->flags = 0; t->init(t->t); } } /* This doesn't free e, because the calling function might need e->next */ void ebt_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); } static char *get_modprobe(void) { int procfile; char *ret; procfile = open(PROC_SYS_MODPROBE, O_RDONLY); if (procfile < 0) return NULL; ret = malloc(1024); if (ret) { if (read(procfile, ret, 1024) == -1) goto fail; /* The kernel adds a '\n' */ ret[1023] = '\n'; *strchr(ret, '\n') = '\0'; close(procfile); return ret; } fail: free(ret); close(procfile); return NULL; } char *ebt_modprobe; /* Try to load the kernel module, analogous to ip_tables.c */ int ebtables_insmod(const char *modname) { char *buf = NULL; char *argv[3]; /* If they don't explicitly set it, read out of /proc */ if (!ebt_modprobe) { buf = get_modprobe(); if (!buf) return -1; ebt_modprobe = buf; /* Keep the value for possible later use */ } switch (fork()) { case 0: argv[0] = (char *)ebt_modprobe; argv[1] = (char *)modname; argv[2] = NULL; execv(argv[0], argv); /* Not usually reached */ exit(0); case -1: return -1; default: /* Parent */ wait(NULL); } return 0; } /* Parse the chain name and return a pointer to the chain base. * Returns NULL on failure. */ struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, const char* arg) { int i; struct ebt_u_entries *chain; for (i = 0; i < replace->num_chains; i++) { if (!(chain = replace->chains[i])) continue; if (!strcmp(arg, chain->name)) return chain; } return NULL; } /* Parse the chain name and return the corresponding chain nr * returns -1 on failure */ int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg) { int i; for (i = 0; i < replace->num_chains; i++) { if (!replace->chains[i]) continue; if (!strcmp(arg, replace->chains[i]->name)) return i; } return -1; } /* ************ ************ **COMMANDS** ************ ************ */ /* Change the policy of selected_chain. * Handing a bad policy to this function is a bug. */ void ebt_change_policy(struct ebt_u_replace *replace, int policy) { struct ebt_u_entries *entries = ebt_to_chain(replace); if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE) ebt_print_bug("Wrong policy: %d", policy); entries->policy = policy; } void ebt_delete_cc(struct ebt_cntchanges *cc) { if (cc->type == CNT_ADD) { cc->prev->next = cc->next; cc->next->prev = cc->prev; free(cc); } else cc->type = CNT_DEL; } void ebt_empty_chain(struct ebt_u_entries *entries) { struct ebt_u_entry *u_e = entries->entries->next, *tmp; while (u_e != entries->entries) { ebt_delete_cc(u_e->cc); ebt_free_u_entry(u_e); tmp = u_e->next; free(u_e); u_e = tmp; } entries->entries->next = entries->entries->prev = entries->entries; entries->nentries = 0; } /* Flush one chain or the complete table * If selected_chain == -1 then flush the complete table */ void ebt_flush_chains(struct ebt_u_replace *replace) { int i, numdel; struct ebt_u_entries *entries = ebt_to_chain(replace); /* Flush whole table */ if (!entries) { if (replace->nentries == 0) return; replace->nentries = 0; /* Free everything and zero (n)entries */ for (i = 0; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; entries->counter_offset = 0; ebt_empty_chain(entries); } return; } if (entries->nentries == 0) return; replace->nentries -= entries->nentries; numdel = entries->nentries; /* Update counter_offset */ for (i = replace->selected_chain+1; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; entries->counter_offset -= numdel; } entries = ebt_to_chain(replace); ebt_empty_chain(entries); } #define OPT_COUNT 0x1000 /* This value is also defined in ebtables.c */ /* Returns the rule number on success (starting from 0), -1 on failure * * This function expects the ebt_{match,watcher,target} members of new_entry * to contain pointers to ebt_u_{match,watcher,target} */ int ebt_check_rule_exists(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry) { 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; struct ebt_u_entries *entries = ebt_to_chain(replace); int i, j, k; u_e = entries->entries->next; /* Check for an existing rule (if there are duplicate rules, * take the first occurance) */ for (i = 0; i < entries->nentries; i++, u_e = u_e->next) { if (u_e->ethproto != new_entry->ethproto) continue; if (strcmp(u_e->in, new_entry->in)) continue; if (strcmp(u_e->out, new_entry->out)) continue; if (strcmp(u_e->logical_in, new_entry->logical_in)) continue; if (strcmp(u_e->logical_out, new_entry->logical_out)) continue; if (new_entry->bitmask & EBT_SOURCEMAC && memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN)) continue; if (new_entry->bitmask & EBT_DESTMAC && memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN)) continue; if (new_entry->bitmask != u_e->bitmask || new_entry->invflags != u_e->invflags) continue; if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt != u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt)) 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; } /* Add a rule, rule_nr is the rule to update * rule_nr specifies where the rule should be inserted * rule_nr > 0 : insert the rule right before the rule_nr'th rule * (the first rule is rule 1) * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule, * where n denotes the number of rules in the chain * rule_nr == 0: add a new rule at the end of the chain * * This function expects the ebt_{match,watcher,target} members of new_entry * to contain pointers to ebt_u_{match,watcher,target} and updates these * pointers so that they point to ebt_{match,watcher,target}, before adding * the rule to the chain. Don't free() the ebt_{match,watcher,target} and * don't reuse the new_entry after a successful call to ebt_add_rule() */ void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr) { int i; struct ebt_u_entry *u_e; struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; struct ebt_u_entries *entries = ebt_to_chain(replace); struct ebt_cntchanges *cc, *new_cc; if (rule_nr <= 0) rule_nr += entries->nentries; else rule_nr--; if (rule_nr > entries->nentries || rule_nr < 0) { ebt_print_error("The specified rule number is incorrect"); return; } /* Go to the right position in the chain */ if (rule_nr == entries->nentries) u_e = entries->entries; else { u_e = entries->entries->next; for (i = 0; i < rule_nr; i++) u_e = u_e->next; } /* We're adding one rule */ replace->nentries++; entries->nentries++; /* Insert the rule */ new_entry->next = u_e; new_entry->prev = u_e->prev; u_e->prev->next = new_entry; u_e->prev = new_entry; new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); if (!new_cc) ebt_print_memory(); new_cc->type = CNT_ADD; new_cc->change = 0; if (new_entry->next == entries->entries) { for (i = replace->selected_chain+1; i < replace->num_chains; i++) if (!replace->chains[i] || replace->chains[i]->nentries == 0) continue; else break; if (i == replace->num_chains) cc = replace->cc; else cc = replace->chains[i]->entries->next->cc; } else cc = new_entry->next->cc; new_cc->next = cc; new_cc->prev = cc->prev; cc->prev->next = new_cc; cc->prev = new_cc; new_entry->cc = new_cc; /* 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; /* Update the counter_offset of chains behind this one */ for (i = replace->selected_chain+1; i < replace->num_chains; i++) { entries = replace->chains[i]; if (!(entries = replace->chains[i])) continue; entries->counter_offset++; } } /* If *begin==*end==0 then find the rule corresponding to new_entry, * else make the rule numbers positive (starting from 0) and check * for bad rule numbers. */ static int check_and_change_rule_number(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int *begin, int *end) { struct ebt_u_entries *entries = ebt_to_chain(replace); if (*begin < 0) *begin += entries->nentries + 1; if (*end < 0) *end += entries->nentries + 1; if (*begin < 0 || *begin > *end || *end > entries->nentries) { ebt_print_error("Sorry, wrong rule numbers"); return -1; } if ((*begin * *end == 0) && (*begin + *end != 0)) ebt_print_bug("begin and end should be either both zero, " "either both non-zero"); if (*begin != 0) { (*begin)--; (*end)--; } else { *begin = ebt_check_rule_exists(replace, new_entry); *end = *begin; if (*begin == -1) { ebt_print_error("Sorry, rule does not exist"); return -1; } } return 0; } /* Delete a rule or rules * begin == end == 0: delete the rule corresponding to new_entry * * The first rule has rule nr 1, the last rule has rule nr -1, etc. * This function expects the ebt_{match,watcher,target} members of new_entry * to contain pointers to ebt_u_{match,watcher,target}. */ void ebt_delete_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int begin, int end) { int i, nr_deletes; struct ebt_u_entry *u_e, *u_e2, *u_e3; struct ebt_u_entries *entries = ebt_to_chain(replace); if (check_and_change_rule_number(replace, new_entry, &begin, &end)) return; /* We're deleting rules */ nr_deletes = end - begin + 1; replace->nentries -= nr_deletes; entries->nentries -= nr_deletes; /* Go to the right position in the chain */ u_e = entries->entries->next; for (i = 0; i < begin; i++) u_e = u_e->next; u_e3 = u_e->prev; /* Remove the rules */ for (i = 0; i < nr_deletes; i++) { u_e2 = u_e; ebt_delete_cc(u_e2->cc); u_e = u_e->next; /* Free everything */ ebt_free_u_entry(u_e2); free(u_e2); } u_e3->next = u_e; u_e->prev = u_e3; /* Update the counter_offset of chains behind this one */ for (i = replace->selected_chain+1; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; entries->counter_offset -= nr_deletes; } } /* Change the counters of a rule or rules * begin == end == 0: change counters of the rule corresponding to new_entry * * The first rule has rule nr 1, the last rule has rule nr -1, etc. * This function expects the ebt_{match,watcher,target} members of new_entry * to contain pointers to ebt_u_{match,watcher,target}. * The mask denotes the following: * pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement * bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment * In daemon mode, mask==0 must hold */ void ebt_change_counters(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int begin, int end, struct ebt_counter *cnt, int mask) { int i; struct ebt_u_entry *u_e; struct ebt_u_entries *entries = ebt_to_chain(replace); if (check_and_change_rule_number(replace, new_entry, &begin, &end)) return; u_e = entries->entries->next; for (i = 0; i < begin; i++) u_e = u_e->next; for (i = end-begin+1; i > 0; i--) { if (mask % 3 == 0) { u_e->cnt.pcnt = (*cnt).pcnt; u_e->cnt_surplus.pcnt = 0; } else { #ifdef EBT_DEBUG if (u_e->cc->type != CNT_NORM) ebt_print_bug("cc->type != CNT_NORM"); #endif u_e->cnt_surplus.pcnt = (*cnt).pcnt; } if (mask / 3 == 0) { u_e->cnt.bcnt = (*cnt).bcnt; u_e->cnt_surplus.bcnt = 0; } else { #ifdef EBT_DEBUG if (u_e->cc->type != CNT_NORM) ebt_print_bug("cc->type != CNT_NORM"); #endif u_e->cnt_surplus.bcnt = (*cnt).bcnt; } if (u_e->cc->type != CNT_ADD) u_e->cc->type = CNT_CHANGE; u_e->cc->change = mask; u_e = u_e->next; } } /* If selected_chain == -1 then zero all counters, * otherwise, zero the counters of selected_chain */ void ebt_zero_counters(struct ebt_u_replace *replace) { struct ebt_u_entries *entries = ebt_to_chain(replace); struct ebt_u_entry *next; int i; if (!entries) { for (i = 0; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; next = entries->entries->next; while (next != entries->entries) { if (next->cc->type == CNT_NORM) next->cc->type = CNT_CHANGE; next->cnt.bcnt = next->cnt.pcnt = 0; next->cc->change = 0; next = next->next; } } } else { if (entries->nentries == 0) return; next = entries->entries->next; while (next != entries->entries) { if (next->cc->type == CNT_NORM) next->cc->type = CNT_CHANGE; next->cnt.bcnt = next->cnt.pcnt = 0; next = next->next; } } } /* Add a new chain and specify its policy */ void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy) { struct ebt_u_entries *new; if (replace->num_chains == replace->max_chains) ebt_double_chains(replace); new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); if (!new) ebt_print_memory(); replace->chains[replace->num_chains++] = new; new->nentries = 0; new->policy = policy; new->counter_offset = replace->nentries; new->hook_mask = 0; strcpy(new->name, name); new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); if (!new->entries) ebt_print_memory(); new->entries->next = new->entries->prev = new->entries; new->kernel_start = NULL; } /* returns -1 if the chain is referenced, 0 on success */ static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err) { int tmp = replace->selected_chain; /* If the chain is referenced, don't delete it, * also decrement jumps to a chain behind the * one we're deleting */ replace->selected_chain = chain; if (ebt_check_for_references(replace, print_err)) return -1; decrease_chain_jumps(replace); ebt_flush_chains(replace); replace->selected_chain = tmp; free(replace->chains[chain]->entries); free(replace->chains[chain]); memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *)); replace->num_chains--; return 0; } /* Selected_chain == -1: delete all non-referenced udc * selected_chain < NF_BR_NUMHOOKS is illegal */ void ebt_delete_chain(struct ebt_u_replace *replace) { if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS) ebt_print_bug("You can't remove a standard chain"); if (replace->selected_chain == -1) { int i = NF_BR_NUMHOOKS; while (i < replace->num_chains) if (ebt_delete_a_chain(replace, i, 0)) i++; } else ebt_delete_a_chain(replace, replace->selected_chain, 1); } /* Rename an existing chain. */ void ebt_rename_chain(struct ebt_u_replace *replace, const char *name) { struct ebt_u_entries *entries = ebt_to_chain(replace); if (!entries) ebt_print_bug("ebt_rename_chain: entries == NULL"); strcpy(entries->name, name); } /* ************************* ************************* **SPECIALIZED*FUNCTIONS** ************************* ************************* */ void ebt_double_chains(struct ebt_u_replace *replace) { struct ebt_u_entries **new; replace->max_chains *= 2; new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *)); if (!new) ebt_print_memory(); memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *)); free(replace->chains); replace->chains = new; } /* Executes the final_check() function for all extensions used by the rule * ebt_check_for_loops should have been executed earlier, to make sure the * hook_mask is correct. The time argument to final_check() is set to 1, * meaning it's the second time the final_check() function is executed. */ void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e, struct ebt_u_entries *entries) { struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; struct ebt_u_target *t; struct ebt_u_match *m; struct ebt_u_watcher *w; m_l = e->m_list; w_l = e->w_list; while (m_l) { m = ebt_find_match(m_l->m->u.name); m->final_check(e, m_l->m, replace->name, entries->hook_mask, 1); if (ebt_errormsg[0] != '\0') return; m_l = m_l->next; } while (w_l) { w = ebt_find_watcher(w_l->w->u.name); w->final_check(e, w_l->w, replace->name, entries->hook_mask, 1); if (ebt_errormsg[0] != '\0') return; w_l = w_l->next; } t = ebt_find_target(e->t->u.name); t->final_check(e, e->t, replace->name, entries->hook_mask, 1); } /* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't. * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ int ebt_check_for_references(struct ebt_u_replace *replace, int print_err) { if (print_err) return iterate_entries(replace, 1); else return iterate_entries(replace, 2); } /* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS) * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't. * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr, int print_err) { int tmp = replace->selected_chain, ret; replace->selected_chain = chain_nr; if (print_err) ret = iterate_entries(replace, 1); else ret = iterate_entries(replace, 2); replace->selected_chain = tmp; return ret; } struct ebt_u_stack { int chain_nr; int n; struct ebt_u_entry *e; struct ebt_u_entries *entries; }; /* Checks for loops * As a by-product, the hook_mask member of each chain is filled in * correctly. The check functions of the extensions need this hook_mask * to know from which standard chains they can be called. */ void ebt_check_for_loops(struct ebt_u_replace *replace) { int chain_nr , i, j , k, sp = 0, verdict; struct ebt_u_entries *entries, *entries2; struct ebt_u_stack *stack = NULL; struct ebt_u_entry *e; /* Initialize hook_mask to 0 */ for (i = 0; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; if (i < NF_BR_NUMHOOKS) /* (1 << NF_BR_NUMHOOKS) implies it's a standard chain * (usefull in the final_check() funtions) */ entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS); else entries->hook_mask = 0; } if (replace->num_chains == NF_BR_NUMHOOKS) return; stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack)); if (!stack) ebt_print_memory(); /* Check for loops, starting from every base chain */ for (i = 0; i < NF_BR_NUMHOOKS; i++) { if (!(entries = replace->chains[i])) continue; chain_nr = i; e = entries->entries->next; for (j = 0; j < entries->nentries; j++) { if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) goto letscontinue; verdict = ((struct ebt_standard_target *)(e->t))->verdict; if (verdict < 0) goto letscontinue; /* Now see if we've been here before */ for (k = 0; k < sp; k++) if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) { ebt_print_error("Loop from chain '%s' to chain '%s'", replace->chains[chain_nr]->name, replace->chains[stack[k].chain_nr]->name); goto free_stack; } entries2 = replace->chains[verdict + NF_BR_NUMHOOKS]; /* check if we've dealt with this chain already */ if (entries2->hook_mask & (1<hook_mask |= entries->hook_mask; /* Jump to the chain, make sure we know how to get back */ stack[sp].chain_nr = chain_nr; stack[sp].n = j; stack[sp].entries = entries; stack[sp].e = e; sp++; j = -1; e = entries2->entries->next; chain_nr = verdict + NF_BR_NUMHOOKS; entries = entries2; continue; letscontinue: e = e->next; } /* We are at the end of a standard chain */ if (sp == 0) continue; /* Go back to the chain one level higher */ sp--; j = stack[sp].n; chain_nr = stack[sp].chain_nr; e = stack[sp].e; entries = stack[sp].entries; goto letscontinue; } free_stack: free(stack); return; } /* The user will use the match, so put it in new_entry. The ebt_u_match * pointer is put in the ebt_entry_match pointer. ebt_add_rule will * fill in the final value for new->m. Unless the rule is added to a chain, * the pointer will keep pointing to the ebt_u_match (until the new_entry * is freed). I know, I should use a union for these 2 pointer types... */ void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m) { struct ebt_u_match_list **m_list, *new; 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) ebt_print_memory(); *m_list = new; new->next = NULL; new->m = (struct ebt_entry_match *)m; } void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w) { struct ebt_u_watcher_list **w_list; struct ebt_u_watcher_list *new; 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) ebt_print_memory(); *w_list = new; new->next = NULL; new->w = (struct ebt_entry_watcher *)w; } /* ******************* ******************* **OTHER*FUNCTIONS** ******************* ******************* */ /* type = 0 => update chain jumps * type = 1 => check for reference, print error when referenced * type = 2 => check for reference, don't print error when referenced * * Returns 1 when type == 1 and the chain is referenced * returns 0 otherwise */ static int iterate_entries(struct ebt_u_replace *replace, int type) { int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS; struct ebt_u_entries *entries; struct ebt_u_entry *e; if (chain_nr < 0) ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr); for (i = 0; i < replace->num_chains; i++) { if (!(entries = replace->chains[i])) continue; e = entries->entries->next; for (j = 0; j < entries->nentries; j++) { int chain_jmp; if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { e = e->next; continue; } chain_jmp = ((struct ebt_standard_target *)e->t)-> verdict; switch (type) { case 1: case 2: if (chain_jmp == chain_nr) { if (type == 2) return 1; ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d", replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, entries->name, j); return 1; } break; case 0: /* Adjust the chain jumps when necessary */ if (chain_jmp > chain_nr) ((struct ebt_standard_target *)e->t)->verdict--; break; } /* End switch */ e = e->next; } } return 0; } static void decrease_chain_jumps(struct ebt_u_replace *replace) { iterate_entries(replace, 0); } /* Used in initialization code of modules */ void ebt_register_match(struct ebt_u_match *m) { int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); struct ebt_u_match **i; m->m = (struct ebt_entry_match *)malloc(size); if (!m->m) ebt_print_memory(); strcpy(m->m->u.name, m->name); m->m->match_size = EBT_ALIGN(m->size); m->init(m->m); for (i = &ebt_matches; *i; i = &((*i)->next)); m->next = NULL; *i = m; } void ebt_register_watcher(struct ebt_u_watcher *w) { int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); struct ebt_u_watcher **i; w->w = (struct ebt_entry_watcher *)malloc(size); if (!w->w) ebt_print_memory(); strcpy(w->w->u.name, w->name); w->w->watcher_size = EBT_ALIGN(w->size); w->init(w->w); for (i = &ebt_watchers; *i; i = &((*i)->next)); w->next = NULL; *i = w; } void ebt_register_target(struct ebt_u_target *t) { int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); struct ebt_u_target **i; t->t = (struct ebt_entry_target *)malloc(size); if (!t->t) ebt_print_memory(); strcpy(t->t->u.name, t->name); t->t->target_size = EBT_ALIGN(t->size); t->init(t->t); for (i = &ebt_targets; *i; i = &((*i)->next)); t->next = NULL; *i = t; } void ebt_register_table(struct ebt_u_table *t) { t->next = ebt_tables; ebt_tables = t; } void ebt_iterate_matches(void (*f)(struct ebt_u_match *)) { struct ebt_u_match *i; for (i = ebt_matches; i; i = i->next) f(i); } void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *)) { struct ebt_u_watcher *i; for (i = ebt_watchers; i; i = i->next) f(i); } void ebt_iterate_targets(void (*f)(struct ebt_u_target *)) { struct ebt_u_target *i; for (i = ebt_targets; i; i = i->next) f(i); } /* Don't use this function, use ebt_print_bug() */ void __ebt_print_bug(char *file, int line, char *format, ...) { va_list l; va_start(l, format); fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line); vfprintf(stderr, format, l); fprintf(stderr, "\n"); va_end(l); exit (-1); } /* The error messages are put in here when ebt_silent == 1 * ebt_errormsg[0] == '\0' implies there was no error */ char ebt_errormsg[ERRORMSG_MAXLEN]; /* When error messages should not be printed on the screen, after which * the program exit()s, set ebt_silent to 1. */ int ebt_silent; /* Don't use this function, use ebt_print_error() */ void __ebt_print_error(char *format, ...) { va_list l; va_start(l, format); if (ebt_silent && ebt_errormsg[0] == '\0') { vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l); va_end(l); } else { vfprintf(stderr, format, l); fprintf(stderr, ".\n"); va_end(l); exit (-1); } } ebtables-v2.0.10-4/ebtables-restore.c0000644000175000017500000001022711672451147017540 0ustar bdschuymbdschuym/* * ebtables-restore.c, October 2005 * * Author: Bart De Schuymer * * 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 #include #include #include #include #include "include/ebtables_u.h" static struct ebt_u_replace replace[3]; void ebt_early_init_once(); #define OPT_KERNELDATA 0x800 /* Also defined in ebtables.c */ static void copy_table_names() { strcpy(replace[0].name, "filter"); strcpy(replace[1].name, "nat"); strcpy(replace[2].name, "broute"); } #define ebtrest_print_error(format, args...) do {fprintf(stderr, "ebtables-restore: "\ "line %d: "format".\n", line, ##args); exit(-1);} while (0) int main(int argc_, char *argv_[]) { char *argv[EBTD_ARGC_MAX], cmdline[EBTD_CMDLINE_MAXLN]; int i, offset, quotemode = 0, argc, table_nr = -1, line = 0, whitespace; char ebtables_str[] = "ebtables"; if (argc_ != 1) ebtrest_print_error("options are not supported"); ebt_silent = 0; copy_table_names(); ebt_early_init_once(); argv[0] = ebtables_str; while (fgets(cmdline, EBTD_CMDLINE_MAXLN, stdin)) { line++; if (*cmdline == '#' || *cmdline == '\n') continue; *strchr(cmdline, '\n') = '\0'; if (*cmdline == '*') { if (table_nr != -1) { ebt_deliver_table(&replace[table_nr]); ebt_deliver_counters(&replace[table_nr]); } for (i = 0; i < 3; i++) if (!strcmp(replace[i].name, cmdline+1)) break; if (i == 3) ebtrest_print_error("table '%s' was not recognized", cmdline+1); table_nr = i; replace[table_nr].command = 11; ebt_get_kernel_table(&replace[table_nr], 1); replace[table_nr].command = 0; replace[table_nr].flags = OPT_KERNELDATA; /* Prevent do_command from initialising replace */ continue; } else if (table_nr == -1) ebtrest_print_error("no table specified"); if (*cmdline == ':') { int policy, chain_nr; char *ch; if (!(ch = strchr(cmdline, ' '))) ebtrest_print_error("no policy specified"); *ch = '\0'; for (i = 0; i < NUM_STANDARD_TARGETS; i++) if (!strcmp(ch+1, ebt_standard_targets[i])) { policy = -i -1; if (policy == EBT_CONTINUE) i = NUM_STANDARD_TARGETS; break; } if (i == NUM_STANDARD_TARGETS) ebtrest_print_error("invalid policy specified"); /* No need to check chain name for consistency, since * we're supposed to be reading an automatically generated * file. */ if ((chain_nr = ebt_get_chainnr(&replace[table_nr], cmdline+1)) == -1) ebt_new_chain(&replace[table_nr], cmdline+1, policy); else replace[table_nr].chains[chain_nr]->policy = policy; continue; } argv[1] = cmdline; offset = whitespace = 0; argc = 2; while (cmdline[offset] != '\0') { if (cmdline[offset] == '\"') { whitespace = 0; quotemode ^= 1; if (quotemode) argv[argc++] = &cmdline[offset+1]; else if (cmdline[offset+1] != ' ' && cmdline[offset+1] != '\0') ebtrest_print_error("syntax error at \""); cmdline[offset] = '\0'; } else if (!quotemode && cmdline[offset] == ' ') { whitespace = 1; cmdline[offset] = '\0'; } else if (whitespace == 1) { argv[argc++] = &cmdline[offset]; whitespace = 0; } offset++; } if (quotemode) ebtrest_print_error("wrong use of '\"'"); optind = 0; /* Setting optind = 1 causes serious annoyances */ do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]); ebt_reinit_extensions(); } if (table_nr != -1) { ebt_deliver_table(&replace[table_nr]); ebt_deliver_counters(&replace[table_nr]); } return 0; } ebtables-v2.0.10-4/ebtables.spec0000644000175000017500000000540611672451150016564 0ustar bdschuymbdschuym# spec file originally from Dag Wieers, altered by Bart De Schuymer %define _sbindir /usr/local/sbin %define _mysysconfdir %{_sysconfdir}/sysconfig Summary: Ethernet Bridge frame table administration tool Name: ebtables Version: 2.0.10 Release: 4 License: GPL Group: System Environment/Base URL: http://ebtables.sourceforge.net/ Packager: Bart De Schuymer Source: http://dl.sf.net/ebtables/ebtables-v%{version}-%{release}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root %description Ethernet bridge tables is a firewalling tool to transparantly filter network traffic passing a bridge. The filtering possibilities are limited to link layer filtering and some basic filtering on higher network layers. The ebtables tool can be used together with the other Linux filtering tools, like iptables. There are no incompatibility issues. %prep %setup -n ebtables-v%{version}-%{release} %build %{__make} %{?_smp_mflags} \ CFLAGS="%{optflags}" %install %{__rm} -rf %{buildroot} %{__install} -D -m0755 ebtables %{buildroot}%{_sbindir}/ebtables %{__install} -D -m0755 ebtables-restore %{buildroot}%{_sbindir}/ebtables-restore %{__install} -D -m0644 ethertypes %{buildroot}%{_sysconfdir}/ethertypes %{__install} -D -m0644 ebtables.8 %{buildroot}%{_mandir}/man8/ebtables.8 %{__mkdir} -p %{buildroot}%{_libdir}/ebtables/ %{__mkdir} -p %{buildroot}%{_sbindir} %{__mkdir} -p %{buildroot}%{_initrddir} %{__mkdir} -p %{buildroot}%{_mysysconfdir} %{__install} -m0755 extensions/*.so %{buildroot}%{_libdir}/ebtables/ %{__install} -m0755 *.so %{buildroot}%{_libdir}/ebtables/ export __iets=`printf %{_sbindir} | sed 's/\\//\\\\\\//g'` export __iets2=`printf %{_mysysconfdir} | sed 's/\\//\\\\\\//g'` sed -i "s/__EXEC_PATH__/$__iets/g" ebtables-save %{__install} -m 0755 -o root -g root ebtables-save %{buildroot}%{_sbindir}/ebtables-save sed -i "s/__EXEC_PATH__/$__iets/g" ebtables.sysv; sed -i "s/__SYSCONFIG__/$__iets2/g" ebtables.sysv %{__install} -m 0755 -o root -g root ebtables.sysv %{buildroot}%{_initrddir}/ebtables sed -i "s/__SYSCONFIG__/$__iets2/g" ebtables-config %{__install} -m 0600 -o root -g root ebtables-config %{buildroot}%{_mysysconfdir}/ebtables-config unset __iets unset __iets2 %clean %{__rm} -rf %{buildroot} %post /sbin/chkconfig --add ebtables %preun if [ $1 -eq 0 ]; then /sbin/service ebtables stop &>/dev/null || : /sbin/chkconfig --del ebtables fi %files %defattr(-, root, root, 0755) %doc ChangeLog COPYING INSTALL THANKS %doc %{_mandir}/man8/ebtables.8* %config %{_sysconfdir}/ethertypes %config %{_mysysconfdir}/ebtables-config %config %{_initrddir}/ebtables %{_sbindir}/ebtables %{_sbindir}/ebtables-save %{_sbindir}/ebtables-restore %{_libdir}/ebtables/ %changelog * Mon Nov 07 2005 Bart De Schuymer - 2.0.8-rc1 - Initial package. ebtables-v2.0.10-4/ethertypes0000644000175000017500000000244511672451147016254 0ustar bdschuymbdschuym# # Ethernet frame types # This file describes some of the various Ethernet # protocol types that are used on Ethernet networks. # # This list could be found on: # http://www.iana.org/assignments/ethernet-numbers # # ... #Comment # IPv4 0800 ip ip4 # Internet IP (IPv4) X25 0805 ARP 0806 ether-arp # FR_ARP 0808 # Frame Relay ARP [RFC1701] BPQ 08FF # G8BPQ AX.25 Ethernet Packet DEC 6000 # DEC Assigned proto DNA_DL 6001 # DEC DNA Dump/Load DNA_RC 6002 # DEC DNA Remote Console DNA_RT 6003 # DEC DNA Routing LAT 6004 # DEC LAT DIAG 6005 # DEC Diagnostics CUST 6006 # DEC Customer use SCA 6007 # DEC Systems Comms Arch TEB 6558 # Trans Ether Bridging [RFC1701] RAW_FR 6559 # Raw Frame Relay [RFC1701] AARP 80F3 # Appletalk AARP ATALK 809B # Appletalk 802_1Q 8100 8021q 1q 802.1q dot1q # 802.1Q Virtual LAN tagged frame IPX 8137 # Novell IPX NetBEUI 8191 # NetBEUI IPv6 86DD ip6 # IP version 6 PPP 880B # PPP ATMMPOA 884C # MultiProtocol over ATM PPP_DISC 8863 # PPPoE discovery messages PPP_SES 8864 # PPPoE session messages ATMFATE 8884 # Frame-based ATM Transport over Ethernet LOOP 9000 loopback # loop proto ebtables-v2.0.10-4/communication.c0000644000175000017500000005403511672451147017150 0ustar bdschuymbdschuym/* * communication.c, v2.0 July 2002 * * Author: Bart De Schuymer * */ /* * All the userspace/kernel communication is in this file. * The other code should not have to know anything about the way the * kernel likes the structure of the table data. * The other code works with linked lists. So, the translation is done here. */ #include #include #include #include #include #include #include #include #include "include/ebtables_u.h" extern char* hooknames[NF_BR_NUMHOOKS]; #ifdef KERNEL_64_USERSPACE_32 #define sparc_cast (uint64_t) #else #define sparc_cast #endif int sockfd = -1; static int get_sockfd() { int ret = 0; if (sockfd == -1) { sockfd = socket(AF_INET, SOCK_RAW, PF_INET); if (sockfd < 0) { ebt_print_error("Problem getting a socket, " "you probably don't have the right " "permissions"); ret = -1; } } return ret; } static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl) { struct ebt_replace *new; struct ebt_u_entry *e; struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; struct ebt_u_entries *entries; char *p, *base; int i, j; unsigned int entries_size = 0, *chain_offsets; new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace)); if (!new) ebt_print_memory(); new->valid_hooks = u_repl->valid_hooks; strcpy(new->name, u_repl->name); new->nentries = u_repl->nentries; new->num_counters = u_repl->num_counters; new->counters = sparc_cast u_repl->counters; chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int)); if (!chain_offsets) ebt_print_memory(); /* Determine size */ for (i = 0; i < u_repl->num_chains; i++) { if (!(entries = u_repl->chains[i])) continue; chain_offsets[i] = entries_size; entries_size += sizeof(struct ebt_entries); j = 0; e = entries->entries->next; while (e != entries->entries) { j++; entries_size += sizeof(struct ebt_entry); m_l = e->m_list; while (m_l) { entries_size += m_l->m->match_size + sizeof(struct ebt_entry_match); m_l = m_l->next; } w_l = e->w_list; while (w_l) { entries_size += w_l->w->watcher_size + sizeof(struct ebt_entry_watcher); w_l = w_l->next; } entries_size += e->t->target_size + sizeof(struct ebt_entry_target); e = e->next; } /* A little sanity check */ if (j != entries->nentries) ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j, entries->nentries, entries->name); } new->entries_size = entries_size; p = (char *)malloc(entries_size); if (!p) ebt_print_memory(); /* Put everything in one block */ new->entries = sparc_cast p; for (i = 0; i < u_repl->num_chains; i++) { struct ebt_entries *hlp; hlp = (struct ebt_entries *)p; if (!(entries = u_repl->chains[i])) continue; if (i < NF_BR_NUMHOOKS) new->hook_entry[i] = sparc_cast hlp; hlp->nentries = entries->nentries; hlp->policy = entries->policy; strcpy(hlp->name, entries->name); hlp->counter_offset = entries->counter_offset; hlp->distinguisher = 0; /* Make the kernel see the light */ p += sizeof(struct ebt_entries); e = entries->entries->next; while (e != entries->entries) { struct ebt_entry *tmp = (struct ebt_entry *)p; tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES; tmp->invflags = e->invflags; tmp->ethproto = e->ethproto; strcpy(tmp->in, e->in); strcpy(tmp->out, e->out); strcpy(tmp->logical_in, e->logical_in); strcpy(tmp->logical_out, e->logical_out); memcpy(tmp->sourcemac, e->sourcemac, sizeof(tmp->sourcemac)); memcpy(tmp->sourcemsk, e->sourcemsk, sizeof(tmp->sourcemsk)); memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac)); memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk)); base = p; p += sizeof(struct ebt_entry); m_l = e->m_list; while (m_l) { memcpy(p, m_l->m, m_l->m->match_size + sizeof(struct ebt_entry_match)); p += m_l->m->match_size + sizeof(struct ebt_entry_match); m_l = m_l->next; } tmp->watchers_offset = p - base; w_l = e->w_list; while (w_l) { memcpy(p, w_l->w, w_l->w->watcher_size + sizeof(struct ebt_entry_watcher)); p += w_l->w->watcher_size + sizeof(struct ebt_entry_watcher); w_l = w_l->next; } tmp->target_offset = p - base; memcpy(p, e->t, e->t->target_size + sizeof(struct ebt_entry_target)); if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { struct ebt_standard_target *st = (struct ebt_standard_target *)p; /* Translate the jump to a udc */ if (st->verdict >= 0) st->verdict = chain_offsets [st->verdict + NF_BR_NUMHOOKS]; } p += e->t->target_size + sizeof(struct ebt_entry_target); tmp->next_offset = p - base; e = e->next; } } /* Sanity check */ if (p - (char *)new->entries != new->entries_size) ebt_print_bug("Entries_size bug"); free(chain_offsets); return new; } static void store_table_in_file(char *filename, struct ebt_replace *repl) { char *data; int size; int fd; /* Start from an empty file with the correct priviliges */ if ((fd = creat(filename, 0600)) == -1) { ebt_print_error("Couldn't create file %s", filename); return; } size = sizeof(struct ebt_replace) + repl->entries_size + repl->nentries * sizeof(struct ebt_counter); data = (char *)malloc(size); if (!data) ebt_print_memory(); memcpy(data, repl, sizeof(struct ebt_replace)); memcpy(data + sizeof(struct ebt_replace), (char *)repl->entries, repl->entries_size); /* Initialize counters to zero, deliver_counters() can update them */ memset(data + sizeof(struct ebt_replace) + repl->entries_size, 0, repl->nentries * sizeof(struct ebt_counter)); if (write(fd, data, size) != size) ebt_print_error("Couldn't write everything to file %s", filename); close(fd); free(data); } void ebt_deliver_table(struct ebt_u_replace *u_repl) { socklen_t optlen; struct ebt_replace *repl; /* Translate the struct ebt_u_replace to a struct ebt_replace */ repl = translate_user2kernel(u_repl); if (u_repl->filename != NULL) { store_table_in_file(u_repl->filename, repl); goto free_repl; } /* Give the data to the kernel */ optlen = sizeof(struct ebt_replace) + repl->entries_size; if (get_sockfd()) goto free_repl; if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) goto free_repl; if (u_repl->command == 8) { /* The ebtables module may not * yet be loaded with --atomic-commit */ ebtables_insmod("ebtables"); if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) goto free_repl; } ebt_print_error("Unable to update the kernel. Two possible causes:\n" "1. Multiple ebtables programs were executing simultaneously. The ebtables\n" " userspace tool doesn't by default support multiple ebtables programs running\n" " concurrently. The ebtables option --concurrent or a tool like flock can be\n" " used to support concurrent scripts that update the ebtables kernel tables.\n" "2. The kernel doesn't support a certain ebtables extension, consider\n" " recompiling your kernel or insmod the extension.\n"); free_repl: if (repl) { free(repl->entries); free(repl); } } static int store_counters_in_file(char *filename, struct ebt_u_replace *repl) { int size = repl->nentries * sizeof(struct ebt_counter), ret = 0; unsigned int entries_size; struct ebt_replace hlp; FILE *file; if (!(file = fopen(filename, "r+b"))) { ebt_print_error("Could not open file %s", filename); return -1; } /* Find out entries_size and then set the file pointer to the * counters */ if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET) || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) != sizeof(unsigned int) || fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) { ebt_print_error("File %s is corrupt", filename); ret = -1; goto close_file; } if (fwrite(repl->counters, sizeof(char), size, file) != size) { ebt_print_error("Could not write everything to file %s", filename); ret = -1; } close_file: fclose(file); return 0; } /* Gets executed after ebt_deliver_table. Delivers the counters to the kernel * and resets the counterchanges to CNT_NORM */ void ebt_deliver_counters(struct ebt_u_replace *u_repl) { struct ebt_counter *old, *new, *newcounters; socklen_t optlen; struct ebt_replace repl; struct ebt_cntchanges *cc = u_repl->cc->next, *cc2; struct ebt_u_entries *entries = NULL; struct ebt_u_entry *next = NULL; int i, chainnr = -1; if (u_repl->nentries == 0) return; newcounters = (struct ebt_counter *) malloc(u_repl->nentries * sizeof(struct ebt_counter)); if (!newcounters) ebt_print_memory(); memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter)); old = u_repl->counters; new = newcounters; while (cc != u_repl->cc) { if (!next || next == entries->entries) { chainnr++; while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) || (next = entries->entries->next) == entries->entries)) chainnr++; if (chainnr == u_repl->num_chains) break; } if (next == NULL) ebt_print_bug("next == NULL"); if (cc->type == CNT_NORM) { /* 'Normal' rule, meaning we didn't do anything to it * So, we just copy */ *new = *old; next->cnt = *new; next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; old++; /* We've used an old counter */ new++; /* We've set a new counter */ next = next->next; } else if (cc->type == CNT_DEL) { old++; /* Don't use this old counter */ } else { if (cc->type == CNT_CHANGE) { if (cc->change % 3 == 1) new->pcnt = old->pcnt + next->cnt_surplus.pcnt; else if (cc->change % 3 == 2) new->pcnt = old->pcnt - next->cnt_surplus.pcnt; else new->pcnt = next->cnt.pcnt; if (cc->change / 3 == 1) new->bcnt = old->bcnt + next->cnt_surplus.bcnt; else if (cc->change / 3 == 2) new->bcnt = old->bcnt - next->cnt_surplus.bcnt; else new->bcnt = next->cnt.bcnt; } else *new = next->cnt; next->cnt = *new; next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; if (cc->type == CNT_ADD) new++; else { old++; new++; } next = next->next; } cc = cc->next; } free(u_repl->counters); u_repl->counters = newcounters; u_repl->num_counters = u_repl->nentries; /* Reset the counterchanges to CNT_NORM and delete the unused cc */ i = 0; cc = u_repl->cc->next; while (cc != u_repl->cc) { if (cc->type == CNT_DEL) { cc->prev->next = cc->next; cc->next->prev = cc->prev; cc2 = cc->next; free(cc); cc = cc2; } else { cc->type = CNT_NORM; cc->change = 0; i++; cc = cc->next; } } if (i != u_repl->nentries) ebt_print_bug("i != u_repl->nentries"); if (u_repl->filename != NULL) { store_counters_in_file(u_repl->filename, u_repl); return; } optlen = u_repl->nentries * sizeof(struct ebt_counter) + sizeof(struct ebt_replace); /* Now put the stuff in the kernel's struct ebt_replace */ repl.counters = sparc_cast u_repl->counters; repl.num_counters = u_repl->num_counters; memcpy(repl.name, u_repl->name, sizeof(repl.name)); if (get_sockfd()) return; if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen)) ebt_print_bug("Couldn't update kernel counters"); } static int ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l) { struct ebt_u_match_list *new; int ret = 0; new = (struct ebt_u_match_list *) malloc(sizeof(struct ebt_u_match_list)); if (!new) ebt_print_memory(); new->m = (struct ebt_entry_match *) malloc(m->match_size + sizeof(struct ebt_entry_match)); if (!new->m) ebt_print_memory(); memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match)); new->next = NULL; **l = new; *l = &new->next; if (ebt_find_match(new->m->u.name) == NULL) { ebt_print_error("Kernel match %s unsupported by userspace tool", new->m->u.name); ret = -1; } return ret; } static int ebt_translate_watcher(struct ebt_entry_watcher *w, struct ebt_u_watcher_list ***l) { struct ebt_u_watcher_list *new; int ret = 0; new = (struct ebt_u_watcher_list *) malloc(sizeof(struct ebt_u_watcher_list)); if (!new) ebt_print_memory(); new->w = (struct ebt_entry_watcher *) malloc(w->watcher_size + sizeof(struct ebt_entry_watcher)); if (!new->w) ebt_print_memory(); memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher)); new->next = NULL; **l = new; *l = &new->next; if (ebt_find_watcher(new->w->u.name) == NULL) { ebt_print_error("Kernel watcher %s unsupported by userspace " "tool", new->w->u.name); ret = -1; } return ret; } static int ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt, int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl, unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc) { /* An entry */ if (e->bitmask & EBT_ENTRY_OR_ENTRIES) { struct ebt_u_entry *new; struct ebt_u_match_list **m_l; struct ebt_u_watcher_list **w_l; struct ebt_entry_target *t; new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); if (!new) ebt_print_memory(); new->bitmask = e->bitmask; /* * Plain userspace code doesn't know about * EBT_ENTRY_OR_ENTRIES */ new->bitmask &= ~EBT_ENTRY_OR_ENTRIES; new->invflags = e->invflags; new->ethproto = e->ethproto; strcpy(new->in, e->in); strcpy(new->out, e->out); strcpy(new->logical_in, e->logical_in); strcpy(new->logical_out, e->logical_out); memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac)); memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk)); memcpy(new->destmac, e->destmac, sizeof(new->destmac)); memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk)); if (*totalcnt >= u_repl->nentries) ebt_print_bug("*totalcnt >= u_repl->nentries"); new->cnt = u_repl->counters[*totalcnt]; new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0; new->cc = *cc; *cc = (*cc)->next; new->m_list = NULL; new->w_list = NULL; new->next = (*u_e)->next; new->next->prev = new; (*u_e)->next = new; new->prev = *u_e; *u_e = new; m_l = &new->m_list; EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l); w_l = &new->w_list; EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l); t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); new->t = (struct ebt_entry_target *) malloc(t->target_size + sizeof(struct ebt_entry_target)); if (!new->t) ebt_print_memory(); if (ebt_find_target(t->u.name) == NULL) { ebt_print_error("Kernel target %s unsupported by " "userspace tool", t->u.name); return -1; } memcpy(new->t, t, t->target_size + sizeof(struct ebt_entry_target)); /* Deal with jumps to udc */ if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) { char *tmp = base; int verdict = ((struct ebt_standard_target *)t)->verdict; int i; if (verdict >= 0) { tmp += verdict; for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++) if (u_repl->chains[i]->kernel_start == tmp) break; if (i == u_repl->num_chains) ebt_print_bug("Can't find udc for jump"); ((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS; } } (*cnt)++; (*totalcnt)++; return 0; } else { /* A new chain */ int i; struct ebt_entries *entries = (struct ebt_entries *)e; if (*n != *cnt) ebt_print_bug("Nr of entries in the chain is wrong"); *n = entries->nentries; *cnt = 0; for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) if (valid_hooks & (1 << i)) break; *hook = i; *u_e = u_repl->chains[*hook]->entries; return 0; } } /* Initialize all chain headers */ static int ebt_translate_chains(struct ebt_entry *e, int *hook, struct ebt_u_replace *u_repl, unsigned int valid_hooks) { int i; struct ebt_entries *entries = (struct ebt_entries *)e; struct ebt_u_entries *new; if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) { for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) if (valid_hooks & (1 << i)) break; new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); if (!new) ebt_print_memory(); if (i == u_repl->max_chains) ebt_double_chains(u_repl); u_repl->chains[i] = new; if (i >= NF_BR_NUMHOOKS) new->kernel_start = (char *)e; *hook = i; new->nentries = entries->nentries; new->policy = entries->policy; new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); if (!new->entries) ebt_print_memory(); new->entries->next = new->entries->prev = new->entries; new->counter_offset = entries->counter_offset; strcpy(new->name, entries->name); } return 0; } static int retrieve_from_file(char *filename, struct ebt_replace *repl, char command) { FILE *file; char *hlp = NULL, *entries; struct ebt_counter *counters; int size, ret = 0; if (!(file = fopen(filename, "r+b"))) { ebt_print_error("Could not open file %s", filename); return -1; } /* Make sure table name is right if command isn't -L or --atomic-commit */ if (command != 'L' && command != 8) { hlp = (char *)malloc(strlen(repl->name) + 1); if (!hlp) ebt_print_memory(); strcpy(hlp, repl->name); } if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file) != sizeof(struct ebt_replace)) { ebt_print_error("File %s is corrupt", filename); ret = -1; goto close_file; } if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) { ebt_print_error("File %s contains wrong table name or is " "corrupt", filename); ret = -1; goto close_file; } else if (!ebt_find_table(repl->name)) { ebt_print_error("File %s contains invalid table name", filename); ret = -1; goto close_file; } size = sizeof(struct ebt_replace) + repl->nentries * sizeof(struct ebt_counter) + repl->entries_size; fseek(file, 0, SEEK_END); if (size != ftell(file)) { ebt_print_error("File %s has wrong size", filename); ret = -1; goto close_file; } entries = (char *)malloc(repl->entries_size); if (!entries) ebt_print_memory(); repl->entries = sparc_cast entries; if (repl->nentries) { counters = (struct ebt_counter *) malloc(repl->nentries * sizeof(struct ebt_counter)); repl->counters = sparc_cast counters; if (!repl->counters) ebt_print_memory(); } else repl->counters = sparc_cast NULL; /* Copy entries and counters */ if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) || fread((char *)repl->entries, sizeof(char), repl->entries_size, file) != repl->entries_size || fseek(file, sizeof(struct ebt_replace) + repl->entries_size, SEEK_SET) || (repl->counters && fread((char *)repl->counters, sizeof(char), repl->nentries * sizeof(struct ebt_counter), file) != repl->nentries * sizeof(struct ebt_counter))) { ebt_print_error("File %s is corrupt", filename); free(entries); repl->entries = NULL; ret = -1; } close_file: fclose(file); free(hlp); return ret; } static int retrieve_from_kernel(struct ebt_replace *repl, char command, int init) { socklen_t optlen; int optname; char *entries; optlen = sizeof(struct ebt_replace); if (get_sockfd()) return -1; /* --atomic-init || --init-table */ if (init) optname = EBT_SO_GET_INIT_INFO; else optname = EBT_SO_GET_INFO; if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) return -1; if ( !(entries = (char *)malloc(repl->entries_size)) ) ebt_print_memory(); repl->entries = sparc_cast entries; if (repl->nentries) { struct ebt_counter *counters; if (!(counters = (struct ebt_counter *) malloc(repl->nentries * sizeof(struct ebt_counter))) ) ebt_print_memory(); repl->counters = sparc_cast counters; } else repl->counters = sparc_cast NULL; /* We want to receive the counters */ repl->num_counters = repl->nentries; optlen += repl->entries_size + repl->num_counters * sizeof(struct ebt_counter); if (init) optname = EBT_SO_GET_INIT_ENTRIES; else optname = EBT_SO_GET_ENTRIES; if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) ebt_print_bug("Hmm, what is wrong??? bug#1"); return 0; } int ebt_get_table(struct ebt_u_replace *u_repl, int init) { int i, j, k, hook; struct ebt_replace repl; struct ebt_u_entry *u_e = NULL; struct ebt_cntchanges *new_cc = NULL, *cc; strcpy(repl.name, u_repl->name); if (u_repl->filename != NULL) { if (init) ebt_print_bug("Getting initial table data from a file is impossible"); if (retrieve_from_file(u_repl->filename, &repl, u_repl->command)) return -1; /* -L with a wrong table name should be dealt with silently */ strcpy(u_repl->name, repl.name); } else if (retrieve_from_kernel(&repl, u_repl->command, init)) return -1; /* Translate the struct ebt_replace to a struct ebt_u_replace */ u_repl->valid_hooks = repl.valid_hooks; u_repl->nentries = repl.nentries; u_repl->num_counters = repl.num_counters; u_repl->counters = repl.counters; u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); if (!u_repl->cc) ebt_print_memory(); u_repl->cc->next = u_repl->cc->prev = u_repl->cc; cc = u_repl->cc; for (i = 0; i < repl.nentries; i++) { new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); if (!new_cc) ebt_print_memory(); new_cc->type = CNT_NORM; new_cc->change = 0; new_cc->prev = cc; cc->next = new_cc; cc = new_cc; } if (repl.nentries) { new_cc->next = u_repl->cc; u_repl->cc->prev = new_cc; } u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *)); u_repl->max_chains = EBT_ORI_MAX_CHAINS; hook = -1; /* FIXME: Clean up when an error is encountered */ EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains, &hook, u_repl, u_repl->valid_hooks); if (hook >= NF_BR_NUMHOOKS) u_repl->num_chains = hook + 1; else u_repl->num_chains = NF_BR_NUMHOOKS; i = 0; /* Holds the expected nr. of entries for the chain */ j = 0; /* Holds the up to now counted entries for the chain */ k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */ cc = u_repl->cc->next; hook = -1; EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size, ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks, (char *)repl.entries, &cc); if (k != u_repl->nentries) ebt_print_bug("Wrong total nentries"); free(repl.entries); return 0; } ebtables-v2.0.10-4/ebtables-save0000644000175000017500000000321011672451147016564 0ustar bdschuymbdschuym#!/usr/bin/perl -w # # # A script that generates text output of the ebtables rules. # Similar to iptables-save. # # It can be used to store active configuration to /etc/sysconfig/ebtables use strict; my $table; my $ebtables = "__EXEC_PATH__/ebtables"; my $cnt = ""; my $version = "1.0"; my $table_name; # ======================================================== # Process filter table # ======================================================== sub process_table { my $chain = ""; my $rules = ""; my $chains = ""; my $line = ""; foreach $line (split("\n",$_[0])) { if ($line =~ m/Bridge table: (.*)/) { print "*$1\n"; next; } if ($line =~ m/Bridge chain: (.*?), entries:.* policy: (.*)/) { $chains = $chains . ":$1 $2\n"; $chain = $1; next; } if ($line =~ m/^$/) { next; } if ($cnt eq "--Lc") { $line =~ s/, pcnt = (.*) -- bcnt = (.*)/-c $1 $2/; } else { $line =~ s/ $//; } $rules = $rules . "-A $chain $line\n"; } print $chains; print $rules; print "\n"; } # ======================================================== unless (-x $ebtables) { exit -1 }; print "# Generated by ebtables-save v$version on " . `date`; if (defined($ENV{'EBTABLES_SAVE_COUNTER'}) && $ENV{'EBTABLES_SAVE_COUNTER'} eq "yes") { $cnt = "--Lc"; } foreach $table_name (split("\n", `grep -E '^ebtable_' /proc/modules | cut -f1 -d' ' | sed s/ebtable_//`)) { $table =`$ebtables -t $table_name -L $cnt`; unless ($? == 0) { print $table; exit -1 }; &process_table($table); } ebtables-v2.0.10-4/ebtables.sysv0000644000175000017500000000657211672451147016651 0ustar bdschuymbdschuym#!/bin/bash # # init script for the Ethernet Bridge filter tables # # Written by Dag Wieers # Modified by Rok Papez # Bart De Schuymer # # chkconfig: - 15 85 # description: Ethernet Bridge filtering tables # # config: __SYSCONFIG__/ebtables (text) # __SYSCONFIG__/ebtables. (binary) source /etc/init.d/functions source /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 [ -x __EXEC_PATH__/ebtables ] || exit 1 [ -x __EXEC_PATH__/ebtables-save ] || exit 1 [ -x __EXEC_PATH__/ebtables-restore ] || exit 1 RETVAL=0 prog="ebtables" desc="Ethernet bridge filtering" umask 0077 #default configuration EBTABLES_TEXT_FORMAT="yes" EBTABLES_BINARY_FORMAT="yes" EBTABLES_MODULES_UNLOAD="yes" EBTABLES_SAVE_ON_STOP="no" EBTABLES_SAVE_ON_RESTART="no" EBTABLES_SAVE_COUNTER="no" config=__SYSCONFIG__/$prog-config [ -f "$config" ] && . "$config" start() { echo -n $"Starting $desc ($prog): " if [ "$EBTABLES_BINARY_FORMAT" = "yes" ]; then for table in $(ls __SYSCONFIG__/ebtables.* 2>/dev/null | sed -e 's/.*ebtables\.//' -e '/save/d' ); do __EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table --atomic-commit || RETVAL=1 done else __EXEC_PATH__/ebtables-restore < /etc/sysconfig/ebtables || RETVAL=1 fi if [ $RETVAL -eq 0 ]; then success "$prog startup" rm -f /var/lock/subsys/$prog else failure "$prog startup" fi echo } stop() { echo -n $"Stopping $desc ($prog): " for table in $(grep '^ebtable_' /proc/modules | sed -e 's/ebtable_\([^ ]*\).*/\1/'); do __EXEC_PATH__/ebtables -t $table --init-table || RETVAL=1 done if [ "$EBTABLES_MODULES_UNLOAD" = "yes" ]; then for mod in $(grep -E '^(ebt|ebtable)_' /proc/modules | cut -f1 -d' ') ebtables; do rmmod $mod 2> /dev/null done fi if [ $RETVAL -eq 0 ]; then success "$prog shutdown" rm -f /var/lock/subsys/$prog else failure "$prog shutdown" fi echo } restart() { stop start } save() { echo -n $"Saving $desc ($prog): " if [ "$EBTABLES_TEXT_FORMAT" = "yes" ]; then if [ -e __SYSCONFIG__/ebtables ]; then chmod 0600 __SYSCONFIG__/ebtables mv -f __SYSCONFIG__/ebtables __SYSCONFIG__/ebtables.save fi __EXEC_PATH__/ebtables-save > __SYSCONFIG__/ebtables || RETVAL=1 fi if [ "$EBTABLES_BINARY_FORMAT" = "yes" ]; then rm -f __SYSCONFIG__/ebtables.*.save for oldtable in $(ls __SYSCONFIG__/ebtables.* 2>/dev/null | grep -vF 'ebtables.save'); do chmod 0600 $oldtable mv -f $oldtable $oldtable.save done for table in $(grep '^ebtable_' /proc/modules | sed -e 's/ebtable_\([^ ]*\).*/\1/'); do __EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table --atomic-save || RETVAL=1 if [ "$EBTABLES_SAVE_COUNTER" = "no" ]; then __EXEC_PATH__/ebtables -t $table --atomic-file __SYSCONFIG__/ebtables.$table -Z || RETVAL=1 fi done fi if [ $RETVAL -eq 0 ]; then success "$prog saved" else failure "$prog saved" fi echo } case "$1" in start) start ;; stop) [ "$EBTABLES_SAVE_ON_STOP" = "yes" ] && save stop ;; restart|reload) [ "$EBTABLES_SAVE_ON_RESTART" = "yes" ] && save restart ;; condrestart) [ -e /var/lock/subsys/$prog ] && restart RETVAL=$? ;; save) save ;; status) __EXEC_PATH__/ebtables-save RETVAL=$? ;; *) echo $"Usage $0 {start|stop|restart|condrestart|save|status}" RETVAL=1 esac exit $RETVAL ebtables-v2.0.10-4/ebtablesu.c0000644000175000017500000000473011672451147016246 0ustar bdschuymbdschuym#include #include #include #include #include #include #include #include static void print_help() { printf("ebtablesu v"PROGVERSION" ("PROGDATE")\n"); printf( "Usage:\n" "ebtablesu open table : copy the kernel table\n" "ebtablesu fopen table file : copy the table from the specified file\n" "ebtablesu free table : remove the table from memory\n" "ebtablesu commit table : commit the table to the kernel\n" "ebtablesu fcommit table file : commit the table to the specified file\n\n" "ebtablesu : the ebtables specifications\n" " use spaces only to separate options and commands\n" "For the ebtables options, see\n# ebtables -h\nor\n# man ebtables\n" ); } int main(int argc, char *argv[]) { char *arguments, *pos; int i, writefd, len = 0; if (argc > EBTD_ARGC_MAX) { fprintf(stderr, "ebtablesd accepts at most %d arguments, %d " "arguments were specified. If you need this many " "arguments, recompile this tool with a higher value for" " EBTD_ARGC_MAX.\n", EBTD_ARGC_MAX - 1, argc - 1); return -1; } else if (argc == 1) { fprintf(stderr, "At least one argument is needed.\n"); print_help(); exit(0); } for (i = 0; i < argc; i++) len += strlen(argv[i]); /* Don't forget '\0' */ len += argc; if (len > EBTD_CMDLINE_MAXLN) { fprintf(stderr, "ebtablesd has a maximum command line argument " "length of %d, an argument length of %d was received. " "If a smaller length is unfeasible, recompile this tool " "with a higher value for EBTD_CMDLINE_MAXLN.\n", EBTD_CMDLINE_MAXLN, len); return -1; } if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { if (argc != 2) { fprintf(stderr, "%s does not accept options.\n", argv[1]); return -1; } print_help(); exit(0); } if (!(arguments = (char *)malloc(len))) { fprintf(stderr, "ebtablesu: out of memory.\n"); return -1; } if ((writefd = open(EBTD_PIPE, O_WRONLY, 0)) == -1) { fprintf(stderr, "Could not open the pipe, perhaps ebtablesd is " "not running or you don't have write permission (try " "running as root).\n"); return -1; } pos = arguments; for (i = 0; i < argc; i++) { strcpy(pos, argv[i]); pos += strlen(argv[i]); *(pos++) = ' '; } *(pos-1) = '\n'; if (write(writefd, arguments, len) == -1) { perror("write"); return -1; } return 0; } ebtables-v2.0.10-4/THANKS0000644000175000017500000000022011672451147015035 0ustar bdschuymbdschuymSpecial thanks go out to these early contributors: Lennert Buytenhek Rusty Russel Harald Welte Jason Lunz Tim Gardner Loïc Minier Nick Fedchik ebtables-v2.0.10-4/ebtables.c0000644000175000017500000012041111672451147016054 0ustar bdschuymbdschuym/* * 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 #include #include #include #include #include #include "include/ebtables_u.h" #include "include/ethernetdb.h" /* Checks whether a command has already been specified */ #define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->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; /* Holds all the data */ static struct ebt_u_replace *replace; /* The chosen table */ static struct ebt_u_table *table; /* The pointers in here are special: * The struct ebt_target pointer is actually a struct ebt_u_target pointer. * I do not feel like using a union. * 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. * The same holds for the struct ebt_match and struct ebt_watcher pointers */ static struct ebt_u_entry *new_entry; static int global_option_offset; #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) ebt_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) ebt_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; } static void merge_match(struct ebt_u_match *m) { ebt_options = merge_options (ebt_options, m->extra_ops, &(m->option_offset)); } static void merge_watcher(struct ebt_u_watcher *w) { ebt_options = merge_options (ebt_options, w->extra_ops, &(w->option_offset)); } static void merge_target(struct ebt_u_target *t) { ebt_options = merge_options (ebt_options, t->extra_ops, &(t->option_offset)); } /* Be backwards compatible, so don't use '+' in kernel */ #define IF_WILDCARD 1 static void print_iface(const char *iface) { char *c; if ((c = strchr(iface, IF_WILDCARD))) *c = '+'; printf("%s ", iface); if (c) *c = IF_WILDCARD; } /* 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 /* Helper function for list_rules() */ static void list_em(struct ebt_u_entries *entries) { 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; if (replace->flags & LIST_MAC2) ebt_printstyle_mac = 2; else ebt_printstyle_mac = 0; hlp = entries->entries->next; if (replace->flags & LIST_X && entries->policy != EBT_ACCEPT) { printf("ebtables -t %s -P %s %s\n", replace->name, entries->name, ebt_standard_targets[-entries->policy - 1]); } else if (!(replace->flags & LIST_X)) { printf("\nBridge chain: %s, entries: %d, policy: %s\n", entries->name, entries->nentries, ebt_standard_targets[-entries->policy - 1]); } if (replace->flags & LIST_N) { i = entries->nentries; while (i > 9) { space++; i /= 10; } } for (i = 0; i < entries->nentries; i++) { if (replace->flags & LIST_N) { digits = 0; /* A little work to get nice rule numbers. */ j = i + 1; while (j > 9) { digits++; j /= 10; } for (j = 0; j < space - digits; j++) printf(" "); printf("%d. ", i + 1); } if (replace->flags & LIST_X) printf("ebtables -t %s -A %s ", replace->name, entries->name); /* The standard target's print() uses this to find out * the name of a udc */ hlp->replace = replace; /* 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("-p "); if (hlp->invflags & EBT_IPROTO) printf("! "); if (hlp->bitmask & EBT_802_3) printf("Length "); else { struct ethertypeent *ent; ent = getethertypebynumber(ntohs(hlp->ethproto)); if (!ent) printf("0x%x ", ntohs(hlp->ethproto)); else printf("%s ", ent->e_name); } } if (hlp->bitmask & EBT_SOURCEMAC) { printf("-s "); if (hlp->invflags & EBT_ISOURCE) printf("! "); ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk); printf(" "); } if (hlp->bitmask & EBT_DESTMAC) { printf("-d "); if (hlp->invflags & EBT_IDEST) printf("! "); ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk); printf(" "); } if (hlp->in[0] != '\0') { printf("-i "); if (hlp->invflags & EBT_IIN) printf("! "); print_iface(hlp->in); } if (hlp->logical_in[0] != '\0') { printf("--logical-in "); if (hlp->invflags & EBT_ILOGICALIN) printf("! "); print_iface(hlp->logical_in); } if (hlp->logical_out[0] != '\0') { printf("--logical-out "); if (hlp->invflags & EBT_ILOGICALOUT) printf("! "); print_iface(hlp->logical_out); } if (hlp->out[0] != '\0') { printf("-o "); if (hlp->invflags & EBT_IOUT) printf("! "); print_iface(hlp->out); } 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 (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET)) printf("%s ", hlp->t->u.name); t = ebt_find_target(hlp->t->u.name); if (!t) ebt_print_bug("Target '%s' not found", hlp->t->u.name); t->print(hlp, hlp->t); if (replace->flags & LIST_C) { uint64_t pcnt = hlp->cnt.pcnt; uint64_t bcnt = hlp->cnt.bcnt; if (replace->flags & LIST_X) printf("-c %"PRIu64" %"PRIu64, pcnt, bcnt); else printf(", pcnt = %"PRIu64" -- bcnt = %"PRIu64, pcnt, bcnt); } printf("\n"); hlp = hlp->next; } } static void print_help() { struct ebt_u_match_list *m_l; struct ebt_u_watcher_list *w_l; PRINT_VERSION; 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 \n" "--atomic-init : put the initial kernel table into \n" "--atomic-save : put the current kernel table into \n" "--atomic-file file : set 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 (see above) will equal its value" "\n\n"); 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(ebt_hooknames); } /* Execute command L */ static void list_rules() { int i; if (!(replace->flags & LIST_X)) printf("Bridge table: %s\n", table->name); if (replace->selected_chain != -1) list_em(ebt_to_chain(replace)); else { /* Create new chains and rename standard chains when necessary */ if (replace->flags & LIST_X && replace->num_chains > NF_BR_NUMHOOKS) { for (i = NF_BR_NUMHOOKS; i < replace->num_chains; i++) printf("ebtables -t %s -N %s\n", replace->name, replace->chains[i]->name); for (i = 0; i < NF_BR_NUMHOOKS; i++) if (replace->chains[i] && strcmp(replace->chains[i]->name, ebt_hooknames[i])) printf("ebtables -t %s -E %s %s\n", replace->name, ebt_hooknames[i], replace->chains[i]->name); } for (i = 0; i < replace->num_chains; i++) if (replace->chains[i]) list_em(replace->chains[i]); } } 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) { 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'))) ebt_print_error2("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) ebt_print_error2("No extra options allowed with -C start_nr[:end_nr] pcnt bcnt"); if (parse_rule_range(argv[optind], rule_nr, rule_nr_end)) ebt_print_error2("Something is wrong with the rule number specification '%s'", argv[optind]); optind++; } if (argv[optind][0] == '+') { if (exec_style == EXEC_STYLE_DAEMON) daemon_incr: ebt_print_error2("Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]); ret += 1; new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10); } else if (argv[optind][0] == '-') { if (exec_style == EXEC_STYLE_DAEMON) daemon_decr: ebt_print_error2("Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]); ret += 2; new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10); } else new_entry->cnt_surplus.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; new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10); } else if (argv[optind][0] == '-') { if (exec_style == EXEC_STYLE_DAEMON) goto daemon_decr; ret += 6; new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10); } else new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10); if (*buffer != '\0') goto invalid; optind++; return ret; invalid: ebt_print_error2("Packet counter '%s' invalid", argv[optind]); } static int parse_iface(char *iface, char *option) { char *c; if ((c = strchr(iface, '+'))) { if (*(c + 1) != '\0') { ebt_print_error("Spurious characters after '+' wildcard for '%s'", option); return -1; } else *c = IF_WILDCARD; } return 0; } void ebt_early_init_once() { ebt_iterate_matches(merge_match); ebt_iterate_watchers(merge_watcher); ebt_iterate_targets(merge_target); } /* signal handler, installed when the option --concurrent is specified. */ static void sighandler(int signum) { exit(-1); } /* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */ int do_command(int argc, char *argv[], int exec_style, struct ebt_u_replace *replace_) { char *buffer; int c, i; int zerochain = -1; /* Needed for the -Z option (we can have -Z -L ) */ int chcounter = 0; /* Needed for -C */ int policy = 0; int rule_nr = 0; int rule_nr_end = 0; 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; struct ebt_u_entries *entries; opterr = 0; ebt_modprobe = NULL; replace = replace_; /* The daemon doesn't use the environment variable */ if (exec_style == EXEC_STYLE_PRG) { buffer = getenv(ATOMIC_ENV_VARIABLE); if (buffer) { replace->filename = malloc(strlen(buffer) + 1); if (!replace->filename) ebt_print_memory(); strcpy(replace->filename, buffer); buffer = NULL; } } replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */ replace->selected_chain = -1; replace->command = 'h'; if (!new_entry) { new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); if (!new_entry) ebt_print_memory(); } /* Put some sane values in our new entry */ ebt_initialize_entry(new_entry); new_entry->replace = replace; /* The scenario induced by this loop makes that: * '-t' ,'-M' and --atomic (if specified) have to come * before '-A' and the like */ /* 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 (replace->command == 'N' && c == 'P') { replace->command = c; optind--; /* No table specified */ goto handle_P; } if (OPT_COMMANDS) ebt_print_error2("Multiple commands are not allowed"); replace->command = c; replace->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') { if (ebt_get_chainnr(replace, optarg) != -1) ebt_print_error2("Chain %s already exists", optarg); else if (ebt_find_target(optarg)) ebt_print_error2("Target with name %s exists", optarg); else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN) ebt_print_error2("Chain name length can't exceed %d", EBT_CHAIN_MAXNAMELEN - 1); else if (strchr(optarg, ' ') != NULL) ebt_print_error2("Use of ' ' not allowed in chain names"); ebt_new_chain(replace, optarg, EBT_ACCEPT); /* This is needed to get -N x -P y working */ replace->selected_chain = ebt_get_chainnr(replace, optarg); break; } else if (c == 'X') { if (optind >= argc) { replace->selected_chain = -1; ebt_delete_chain(replace); break; } if (optind < argc - 1) ebt_print_error2("No extra options allowed with -X"); if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1) ebt_print_error2("Chain '%s' doesn't exist", argv[optind]); ebt_delete_chain(replace); if (ebt_errormsg[0] != '\0') return -1; optind++; break; } if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1) ebt_print_error2("Chain '%s' doesn't exist", optarg); if (c == 'E') { if (optind >= argc) ebt_print_error2("No new chain name specified"); else if (optind < argc - 1) ebt_print_error2("No extra options allowed with -E"); else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN) ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1); else if (ebt_get_chainnr(replace, argv[optind]) != -1) ebt_print_error2("Chain '%s' already exists", argv[optind]); else if (ebt_find_target(argv[optind])) ebt_print_error2("Target with name '%s' exists", argv[optind]); else if (strchr(argv[optind], ' ') != NULL) ebt_print_error2("Use of ' ' not allowed in chain names"); ebt_rename_chain(replace, argv[optind]); optind++; break; } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) { if (optind != argc - 1) ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]"); if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end)) ebt_print_error2("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)) == -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 = strtol(argv[optind], &buffer, 10); if (*buffer != '\0') ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]); optind++; } } else if (c == 'P') { handle_P: if (optind >= argc) ebt_print_error2("No policy specified"); for (i = 0; i < NUM_STANDARD_TARGETS; i++) if (!strcmp(argv[optind], ebt_standard_targets[i])) { policy = -i -1; if (policy == EBT_CONTINUE) ebt_print_error2("Wrong policy '%s'", argv[optind]); break; } if (i == NUM_STANDARD_TARGETS) ebt_print_error2("Unknown policy '%s'", argv[optind]); optind++; } break; case 'L': /* List */ case 'F': /* Flush */ case 'Z': /* Zero counters */ if (c == 'Z') { if ((replace->flags & OPT_ZERO) || (replace->flags & OPT_COMMAND && replace->command != 'L')) print_zero: ebt_print_error2("Command -Z only allowed together with command -L"); replace->flags |= OPT_ZERO; } else { if (replace->flags & OPT_COMMAND) ebt_print_error2("Multiple commands are not allowed"); replace->command = c; replace->flags |= OPT_COMMAND; if (replace->flags & OPT_ZERO && c != 'L') goto print_zero; } #ifdef SILENT_DAEMON if (c== 'L' && exec_style == EXEC_STYLE_DAEMON) ebt_print_error2("-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) ebt_print_error2("Multiple commands are not allowed"); replace->command = 'V'; if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error2(PROGNAME" v"PROGVERSION" ("PROGDATE")\n"); PRINT_VERSION; exit(0); case 'M': /* Modprobe */ if (OPT_COMMANDS) ebt_print_error2("Please put the -M option earlier"); free(ebt_modprobe); ebt_modprobe = optarg; break; case 'h': /* Help */ #ifdef SILENT_DAEMON if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error2("-h not supported in daemon mode"); #endif if (OPT_COMMANDS) ebt_print_error2("Multiple commands are 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 (!strcasecmp("list_extensions", argv[optind])) { ebt_list_extensions(); 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 = ebt_find_target(argv[optind]))) ebt_print_error2("Extension '%s' not found", argv[optind]); if (replace->flags & OPT_JUMP) ebt_print_error2("Sorry, you can only see help for one target extension at a time"); replace->flags |= OPT_JUMP; new_entry->t = (struct ebt_entry_target *)t; } optind++; } break; case 't': /* Table */ if (OPT_COMMANDS) ebt_print_error2("Please put the -t option first"); ebt_check_option2(&(replace->flags), OPT_TABLE); if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1) ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1); 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 */ case 'c': /* Set counters */ if (!OPT_COMMANDS) ebt_print_error2("No command specified"); if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I' && replace->command != 'C') ebt_print_error2("Command and option do not match"); if (c == 'i') { ebt_check_option2(&(replace->flags), OPT_IN); if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains"); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_IIN; if (strlen(optarg) >= IFNAMSIZ) big_iface_length: ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1); strcpy(new_entry->in, optarg); if (parse_iface(new_entry->in, "-i")) return -1; break; } else if (c == 2) { ebt_check_option2(&(replace->flags), OPT_LOGICALIN); if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains"); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_ILOGICALIN; if (strlen(optarg) >= IFNAMSIZ) goto big_iface_length; strcpy(new_entry->logical_in, optarg); if (parse_iface(new_entry->logical_in, "--logical-in")) return -1; break; } else if (c == 'o') { ebt_check_option2(&(replace->flags), OPT_OUT); if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains"); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_IOUT; if (strlen(optarg) >= IFNAMSIZ) goto big_iface_length; strcpy(new_entry->out, optarg); if (parse_iface(new_entry->out, "-o")) return -1; break; } else if (c == 3) { ebt_check_option2(&(replace->flags), OPT_LOGICALOUT); if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains"); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_ILOGICALOUT; if (strlen(optarg) >= IFNAMSIZ) goto big_iface_length; strcpy(new_entry->logical_out, optarg); if (parse_iface(new_entry->logical_out, "--logical-out")) return -1; break; } else if (c == 'j') { ebt_check_option2(&(replace->flags), OPT_JUMP); for (i = 0; i < NUM_STANDARD_TARGETS; i++) if (!strcmp(optarg, ebt_standard_targets[i])) { t = ebt_find_target(EBT_STANDARD_TARGET); ((struct ebt_standard_target *) t->t)->verdict = -i - 1; break; } if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) { ebt_print_error2("Return target only for user defined chains"); } else if (i != NUM_STANDARD_TARGETS) break; if ((i = ebt_get_chainnr(replace, optarg)) != -1) { if (i < NF_BR_NUMHOOKS) ebt_print_error2("Don't jump to a standard chain"); t = ebt_find_target(EBT_STANDARD_TARGET); ((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS; break; } else { /* Must be an extension then */ struct ebt_u_target *t; t = ebt_find_target(optarg); /* -j standard not allowed either */ if (!t || t == (struct ebt_u_target *)new_entry->t) ebt_print_error2("Illegal target name '%s'", optarg); new_entry->t = (struct ebt_entry_target *)t; ebt_find_target(EBT_STANDARD_TARGET)->used = 0; t->used = 1; } break; } else if (c == 's') { ebt_check_option2(&(replace->flags), OPT_SOURCE); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_ISOURCE; if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk)) ebt_print_error2("Problem with specified source mac '%s'", optarg); new_entry->bitmask |= EBT_SOURCEMAC; break; } else if (c == 'd') { ebt_check_option2(&(replace->flags), OPT_DEST); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_IDEST; if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk)) ebt_print_error2("Problem with specified destination mac '%s'", optarg); new_entry->bitmask |= EBT_DESTMAC; break; } else if (c == 'c') { ebt_check_option2(&(replace->flags), OPT_COUNT); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected '!' after -c"); if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-') ebt_print_error2("Option -c needs 2 arguments"); new_entry->cnt.pcnt = strtoull(optarg, &buffer, 10); if (*buffer != '\0') ebt_print_error2("Packet counter '%s' invalid", optarg); new_entry->cnt.bcnt = strtoull(argv[optind], &buffer, 10); if (*buffer != '\0') ebt_print_error2("Packet counter '%s' invalid", argv[optind]); optind++; break; } ebt_check_option2(&(replace->flags), OPT_PROTOCOL); if (ebt_check_inverse2(optarg)) new_entry->invflags |= EBT_IPROTO; new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO); i = strtol(optarg, &buffer, 16); if (*buffer == '\0' && (i < 0 || i > 0xFFFF)) ebt_print_error2("Problem with the specified protocol"); if (*buffer != '\0') { struct ethertypeent *ent; if (!strcasecmp(optarg, "LENGTH")) { new_entry->bitmask |= EBT_802_3; break; } ent = getethertypebyname(optarg); if (!ent) ebt_print_error2("Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg); new_entry->ethproto = ent->e_ethertype; } else new_entry->ethproto = i; if (new_entry->ethproto < 0x0600) ebt_print_error2("Sorry, protocols have values above or equal to 0x0600"); break; case 4 : /* Lc */ #ifdef SILENT_DAEMON if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error2("--Lc is not supported in daemon mode"); #endif ebt_check_option2(&(replace->flags), LIST_C); if (replace->command != 'L') ebt_print_error("Use --Lc with -L"); replace->flags |= LIST_C; break; case 5 : /* Ln */ #ifdef SILENT_DAEMON if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error2("--Ln is not supported in daemon mode"); #endif ebt_check_option2(&(replace->flags), LIST_N); if (replace->command != 'L') ebt_print_error2("Use --Ln with -L"); if (replace->flags & LIST_X) ebt_print_error2("--Lx is not compatible with --Ln"); replace->flags |= LIST_N; break; case 6 : /* Lx */ #ifdef SILENT_DAEMON if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error2("--Lx is not supported in daemon mode"); #endif ebt_check_option2(&(replace->flags), LIST_X); if (replace->command != 'L') ebt_print_error2("Use --Lx with -L"); if (replace->flags & LIST_N) ebt_print_error2("--Lx is not compatible with --Ln"); replace->flags |= LIST_X; break; case 12 : /* Lmac2 */ #ifdef SILENT_DAEMON if (exec_style == EXEC_STYLE_DAEMON) ebt_print_error("--Lmac2 is not supported in daemon mode"); #endif ebt_check_option2(&(replace->flags), LIST_MAC2); if (replace->command != 'L') ebt_print_error2("Use --Lmac2 with -L"); replace->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); else ebt_print_error2("Bad argument : '%s'", optarg); /* ebt_check_inverse() 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 (replace->command != 'A' && replace->command != 'I' && replace->command != 'D' && replace->command != 'C') ebt_print_error2("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 (replace->command == 'h' && !(replace->flags & OPT_ZERO)) { print_help(); 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 */ new_entry->ethproto = htons(new_entry->ethproto); if (replace->command == 'P') { if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN) ebt_print_error2("Policy RETURN only allowed for user defined chains"); ebt_change_policy(replace, policy); if (ebt_errormsg[0] != '\0') return -1; } else if (replace->command == 'L') { list_rules(); if (!(replace->flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG) exit(0); } if (replace->flags & OPT_ZERO) { replace->selected_chain = zerochain; ebt_zero_counters(replace); } else if (replace->command == 'F') { ebt_flush_chains(replace); } else if (replace->command == 'A' || replace->command == 'I') { ebt_add_rule(replace, new_entry, rule_nr); if (ebt_errormsg[0] != '\0') return -1; /* Makes undoing the add easier (jumps to delete_the_rule) */ if (rule_nr <= 0) rule_nr--; rule_nr_end = rule_nr; /* a jump to a udc requires checking for loops */ if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) && ((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) { /* FIXME: this can be done faster */ ebt_check_for_loops(replace); if (ebt_errormsg[0] != '\0') goto delete_the_rule; } /* Do the final_check(), for all entries. * This is needed when adding a rule that has a chain target */ i = -1; while (++i != replace->num_chains) { struct ebt_u_entry *e; entries = replace->chains[i]; if (!entries) { if (i < NF_BR_NUMHOOKS) continue; else ebt_print_bug("whoops\n"); } e = entries->entries->next; while (e != entries->entries) { /* Userspace extensions use host endian */ e->ethproto = ntohs(e->ethproto); ebt_do_final_checks(replace, e, entries); if (ebt_errormsg[0] != '\0') goto delete_the_rule; e->ethproto = htons(e->ethproto); e = e->next; } } /* Don't reuse the added rule */ new_entry = NULL; } else if (replace->command == 'D') { delete_the_rule: ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end); if (ebt_errormsg[0] != '\0') return -1; } 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 0; } ebtables-v2.0.10-4/COPYING0000644000175000017500000004327611672451147015177 0ustar bdschuymbdschuymAll code in this package, including the code from the extensions, is released under the GPL license, which you find hereafter. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ebtables-v2.0.10-4/extensions/0000755000175000017500000000000011672451147016327 5ustar bdschuymbdschuymebtables-v2.0.10-4/extensions/ebt_nat.c0000644000175000017500000001440111672451147020107 0ustar bdschuymbdschuym/* ebt_nat * * Authors: * Bart De Schuymer * * June, 2002 */ #include #include #include #include #include "../include/ebtables_u.h" #include #include static int to_source_supplied, to_dest_supplied; #define NAT_S '1' #define NAT_D '1' #define NAT_S_TARGET '2' #define NAT_D_TARGET '2' #define NAT_S_ARP '3' static struct option opts_s[] = { { "to-source" , required_argument, 0, NAT_S }, { "to-src" , required_argument, 0, NAT_S }, { "snat-target" , required_argument, 0, NAT_S_TARGET }, { "snat-arp" , no_argument, 0, NAT_S_ARP }, { 0 } }; static struct option opts_d[] = { { "to-destination", required_argument, 0, NAT_D }, { "to-dst" , required_argument, 0, NAT_D }, { "dnat-target" , required_argument, 0, NAT_D_TARGET }, { 0 } }; static void print_help_s() { printf( "snat options:\n" " --to-src address : MAC address to map source to\n" " --snat-target target : ACCEPT, DROP, RETURN or CONTINUE\n" " --snat-arp : also change src address in arp msg\n"); } static void print_help_d() { printf( "dnat options:\n" " --to-dst address : MAC address to map destination to\n" " --dnat-target target : ACCEPT, DROP, RETURN or CONTINUE\n"); } static void init_s(struct ebt_entry_target *target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; to_source_supplied = 0; natinfo->target = EBT_ACCEPT; return; } static void init_d(struct ebt_entry_target *target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; to_dest_supplied = 0; natinfo->target = EBT_ACCEPT; return; } #define OPT_SNAT 0x01 #define OPT_SNAT_TARGET 0x02 #define OPT_SNAT_ARP 0x04 static int parse_s(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_target **target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data; struct ether_addr *addr; switch (c) { case NAT_S: ebt_check_option2(flags, OPT_SNAT); to_source_supplied = 1; if (!(addr = ether_aton(optarg))) ebt_print_error2("Problem with specified --to-source mac"); memcpy(natinfo->mac, addr, ETH_ALEN); break; case NAT_S_TARGET: { int tmp; ebt_check_option2(flags, OPT_SNAT_TARGET); if (FILL_TARGET(optarg, tmp)) ebt_print_error2("Illegal --snat-target target"); natinfo->target = (natinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS); } break; case NAT_S_ARP: ebt_check_option2(flags, OPT_SNAT_ARP); natinfo->target ^= NAT_ARP_BIT; break; default: return 0; } return 1; } #define OPT_DNAT 0x01 #define OPT_DNAT_TARGET 0x02 static int parse_d(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_target **target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data; struct ether_addr *addr; switch (c) { case NAT_D: ebt_check_option2(flags, OPT_DNAT); to_dest_supplied = 1; if (!(addr = ether_aton(optarg))) ebt_print_error2("Problem with specified --to-destination mac"); memcpy(natinfo->mac, addr, ETH_ALEN); break; case NAT_D_TARGET: ebt_check_option2(flags, OPT_DNAT_TARGET); if (FILL_TARGET(optarg, natinfo->target)) ebt_print_error2("Illegal --dnat-target target"); break; default: return 0; } return 1; } static void final_check_s(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hookmask, unsigned int time) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; if (BASE_CHAIN && (natinfo->target | ~EBT_VERDICT_BITS) == EBT_RETURN) { ebt_print_error("--snat-target RETURN not allowed on base chain"); return; } CLEAR_BASE_CHAIN_BIT; if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat")) { ebt_print_error("Wrong chain for snat"); } else if (time == 0 && to_source_supplied == 0) ebt_print_error("No snat address supplied"); } static void final_check_d(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hookmask, unsigned int time) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; if (BASE_CHAIN && natinfo->target == EBT_RETURN) { ebt_print_error("--dnat-target RETURN not allowed on base chain"); return; } CLEAR_BASE_CHAIN_BIT; if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) || strcmp(name, "nat")) && ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute"))) { ebt_print_error("Wrong chain for dnat"); } else if (time == 0 && to_dest_supplied == 0) ebt_print_error("No dnat address supplied"); } static void print_s(const struct ebt_u_entry *entry, const struct ebt_entry_target *target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; printf("--to-src "); ebt_print_mac(natinfo->mac); if (!(natinfo->target&NAT_ARP_BIT)) printf(" --snat-arp"); printf(" --snat-target %s", TARGET_NAME((natinfo->target|~EBT_VERDICT_BITS))); } static void print_d(const struct ebt_u_entry *entry, const struct ebt_entry_target *target) { struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; printf("--to-dst "); ebt_print_mac(natinfo->mac); printf(" --dnat-target %s", TARGET_NAME(natinfo->target)); } static int compare(const struct ebt_entry_target *t1, const struct ebt_entry_target *t2) { struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data; struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data; return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac)) && natinfo1->target == natinfo2->target; } static struct ebt_u_target snat_target = { .name = "snat", .size = sizeof(struct ebt_nat_info), .help = print_help_s, .init = init_s, .parse = parse_s, .final_check = final_check_s, .print = print_s, .compare = compare, .extra_ops = opts_s, }; static struct ebt_u_target dnat_target = { .name = "dnat", .size = sizeof(struct ebt_nat_info), .help = print_help_d, .init = init_d, .parse = parse_d, .final_check = final_check_d, .print = print_d, .compare = compare, .extra_ops = opts_d, }; void _init(void) { ebt_register_target(&snat_target); ebt_register_target(&dnat_target); } ebtables-v2.0.10-4/extensions/ebt_802_3.c0000644000175000017500000000701611672451147020064 0ustar bdschuymbdschuym/* 802_3 * * Author: * Chris Vitale * * May 2003 */ #include #include #include #include #include "../include/ebtables_u.h" #include "../include/ethernetdb.h" #include #define _802_3_SAP '1' #define _802_3_TYPE '2' static struct option opts[] = { { "802_3-sap" , required_argument, 0, _802_3_SAP }, { "802_3-type" , required_argument, 0, _802_3_TYPE }, { 0 } }; static void print_help() { printf( "802_3 options:\n" "--802_3-sap [!] protocol : 802.3 DSAP/SSAP- 1 byte value (hex)\n" " DSAP and SSAP are always the same. One SAP applies to both fields\n" "--802_3-type [!] protocol : 802.3 SNAP Type- 2 byte value (hex)\n" " Type implies SAP value 0xaa\n"); } static void init(struct ebt_entry_match *match) { struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data; info->invflags = 0; info->bitmask = 0; } static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data; unsigned int i; char *end; switch (c) { case _802_3_SAP: ebt_check_option2(flags, _802_3_SAP); if (ebt_check_inverse2(optarg)) info->invflags |= EBT_802_3_SAP; i = strtoul(optarg, &end, 16); if (i > 255 || *end != '\0') ebt_print_error2("Problem with specified " "sap hex value, %x",i); info->sap = i; /* one byte, so no byte order worries */ info->bitmask |= EBT_802_3_SAP; break; case _802_3_TYPE: ebt_check_option2(flags, _802_3_TYPE); if (ebt_check_inverse2(optarg)) info->invflags |= EBT_802_3_TYPE; i = strtoul(optarg, &end, 16); if (i > 65535 || *end != '\0') { ebt_print_error2("Problem with the specified " "type hex value, %x",i); } info->type = htons(i); info->bitmask |= EBT_802_3_TYPE; break; default: return 0; } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { if (!(entry->bitmask & EBT_802_3)) ebt_print_error("For 802.3 DSAP/SSAP filtering the protocol " "must be LENGTH"); } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data; if (info->bitmask & EBT_802_3_SAP) { printf("--802_3-sap "); if (info->invflags & EBT_802_3_SAP) printf("! "); printf("0x%.2x ", info->sap); } if (info->bitmask & EBT_802_3_TYPE) { printf("--802_3-type "); if (info->invflags & EBT_802_3_TYPE) printf("! "); printf("0x%.4x ", ntohs(info->type)); } } static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2) { struct ebt_802_3_info *info1 = (struct ebt_802_3_info *)m1->data; struct ebt_802_3_info *info2 = (struct ebt_802_3_info *)m2->data; if (info1->bitmask != info2->bitmask) return 0; if (info1->invflags != info2->invflags) return 0; if (info1->bitmask & EBT_802_3_SAP) { if (info1->sap != info2->sap) return 0; } if (info1->bitmask & EBT_802_3_TYPE) { if (info1->type != info2->type) return 0; } return 1; } static struct ebt_u_match _802_3_match = { .name = "802_3", .size = sizeof(struct ebt_802_3_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_match(&_802_3_match); } ebtables-v2.0.10-4/extensions/ebt_standard.c0000644000175000017500000000363311672451147021132 0ustar bdschuymbdschuym/* ebt_standard * * Authors: * Bart De Schuymer * * April, 2002 */ #include #include #include #include "../include/ebtables_u.h" static struct option opts[] = { {0} }; static void print_help() { printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n" "The target can also be a user defined chain.\n"); } static void init(struct ebt_entry_target *t) { ((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE; } static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_target **target) { return 0; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_target *target, const char *name, unsigned int hookmask, unsigned int time) { } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_target *target) { int verdict = ((struct ebt_standard_target *)target)->verdict; if (verdict >= 0) { struct ebt_u_entries *entries; entries = entry->replace->chains[verdict + NF_BR_NUMHOOKS]; printf("%s", entries->name); return; } if (verdict == EBT_CONTINUE) printf("CONTINUE "); else if (verdict == EBT_ACCEPT) printf("ACCEPT "); else if (verdict == EBT_DROP) printf("DROP "); else if (verdict == EBT_RETURN) printf("RETURN "); else ebt_print_bug("Bad standard target"); } static int compare(const struct ebt_entry_target *t1, const struct ebt_entry_target *t2) { return ((struct ebt_standard_target *)t1)->verdict == ((struct ebt_standard_target *)t2)->verdict; } static struct ebt_u_target standard = { .name = "standard", .size = sizeof(struct ebt_standard_target) - sizeof(struct ebt_entry_target), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_target(&standard); } ebtables-v2.0.10-4/extensions/ebt_among.c0000644000175000017500000002710211672451147020430 0ustar bdschuymbdschuym/* ebt_among * * Authors: * Grzegorz Borowiak * * August, 2003 */ #include #include #include #include #include #include #include "../include/ebtables_u.h" #include #include "../include/ethernetdb.h" #include #include #include #include #include #define AMONG_DST '1' #define AMONG_SRC '2' #define AMONG_DST_F '3' #define AMONG_SRC_F '4' static struct option opts[] = { {"among-dst", required_argument, 0, AMONG_DST}, {"among-src", required_argument, 0, AMONG_SRC}, {"among-dst-file", required_argument, 0, AMONG_DST_F}, {"among-src-file", required_argument, 0, AMONG_SRC_F}, {0} }; #ifdef DEBUG static void hexdump(const void *mem, int howmany) { printf("\n"); const unsigned char *p = mem; int i; for (i = 0; i < howmany; i++) { if (i % 32 == 0) { printf("\n%04x: ", i); } printf("%2.2x%c", p[i], ". "[i % 4 == 3]); } printf("\n"); } #endif /* DEBUG */ static void print_help() { printf( "`among' options:\n" "--among-dst [!] list : matches if ether dst is in list\n" "--among-src [!] list : matches if ether src is in list\n" "--among-dst-file [!] file : obtain dst list from file\n" "--among-src-file [!] file : obtain src list from file\n" "list has form:\n" " xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]" ",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n" "Things in brackets are optional.\n" "If you want to allow two (or more) IP addresses to one MAC address, you\n" "can specify two (or more) pairs with the same MAC, e.g.\n" " 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n" ); } static int old_size; static void init(struct ebt_entry_match *match) { struct ebt_among_info *amonginfo = (struct ebt_among_info *) match->data; memset(amonginfo, 0, sizeof(struct ebt_among_info)); old_size = sizeof(struct ebt_among_info); } static struct ebt_mac_wormhash *new_wormhash(int n) { int size = sizeof(struct ebt_mac_wormhash) + n * sizeof(struct ebt_mac_wormhash_tuple); struct ebt_mac_wormhash *result = (struct ebt_mac_wormhash *) malloc(size); if (!result) ebt_print_memory(); memset(result, 0, size); result->poolsize = n; return result; } static void copy_wormhash(struct ebt_mac_wormhash *d, const struct ebt_mac_wormhash *s) { int dpoolsize = d->poolsize; int dsize, ssize, amount; dsize = ebt_mac_wormhash_size(d); ssize = ebt_mac_wormhash_size(s); amount = dsize < ssize ? dsize : ssize; memcpy(d, s, amount); d->poolsize = dpoolsize; } /* Returns: * -1 when '\0' reached * -2 when `n' bytes read and no delimiter found * 0 when no less than `n' bytes read and delimiter found * if `destbuf' is not NULL, it is filled by read bytes and ended with '\0' * *pp is set on the first byte not copied to `destbuf' */ static int read_until(const char **pp, const char *delimiters, char *destbuf, int n) { int count = 0; int ret = 0; char c; while (1) { c = **pp; if (!c) { ret = -1; break; } if (strchr(delimiters, c)) { ret = 0; break; } if (count == n) { ret = -2; break; } if (destbuf) destbuf[count++] = c; (*pp)++; } if (destbuf) destbuf[count] = 0; return ret; } static int fcmp(const void *va, const void *vb) { const struct ebt_mac_wormhash_tuple *a = va; const struct ebt_mac_wormhash_tuple *b = vb; int ca = ((const unsigned char*)a->cmp)[7]; int cb = ((const unsigned char*)b->cmp)[7]; return ca - cb; } static void index_table(struct ebt_mac_wormhash *wh) { int ipool, itable; int c; for (itable = 0; itable <= 256; itable++) { wh->table[itable] = wh->poolsize; } ipool = 0; itable = 0; while (1) { wh->table[itable] = ipool; c = ((const unsigned char*)wh->pool[ipool].cmp)[7]; if (itable <= c) { itable++; } else { ipool++; } if (ipool > wh->poolsize) break; } } static struct ebt_mac_wormhash *create_wormhash(const char *arg) { const char *pc = arg; const char *anchor; char *endptr; struct ebt_mac_wormhash *workcopy, *result, *h; unsigned char mac[6]; unsigned char ip[4]; int nmacs = 0; int i; char token[4]; if (!(workcopy = new_wormhash(1024))) { ebt_print_memory(); } while (1) { /* remember current position, we'll need it on error */ anchor = pc; /* collect MAC; all its bytes are followed by ':' (colon), * except for the last one which can be followed by * ',' (comma), '=' or '\0' */ for (i = 0; i < 5; i++) { if (read_until(&pc, ":", token, 2) < 0 || token[0] == 0) { ebt_print_error("MAC parse error: %.20s", anchor); free(workcopy); return NULL; } mac[i] = strtol(token, &endptr, 16); if (*endptr) { ebt_print_error("MAC parse error: %.20s", anchor); free(workcopy); return NULL; } pc++; } if (read_until(&pc, "=,", token, 2) == -2 || token[0] == 0) { ebt_print_error("MAC parse error: %.20s", anchor); return NULL; } mac[i] = strtol(token, &endptr, 16); if (*endptr) { ebt_print_error("MAC parse error: %.20s", anchor); return NULL; } if (*pc == '=') { /* an IP follows the MAC; collect similarly to MAC */ pc++; anchor = pc; for (i = 0; i < 3; i++) { if (read_until(&pc, ".", token, 3) < 0 || token[0] == 0) { ebt_print_error("IP parse error: %.20s", anchor); return NULL; } ip[i] = strtol(token, &endptr, 10); if (*endptr) { ebt_print_error("IP parse error: %.20s", anchor); return NULL; } pc++; } if (read_until(&pc, ",", token, 3) == -2 || token[0] == 0) { ebt_print_error("IP parse error: %.20s", anchor); return NULL; } ip[3] = strtol(token, &endptr, 10); if (*endptr) { ebt_print_error("IP parse error: %.20s", anchor); return NULL; } if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { ebt_print_error("Illegal IP 0.0.0.0"); return NULL; } } else { /* no IP, we set it to 0.0.0.0 */ memset(ip, 0, 4); } /* we have collected MAC and IP, so we add an entry */ memcpy(((char *) workcopy->pool[nmacs].cmp) + 2, mac, 6); memcpy(&(workcopy->pool[nmacs].ip), ip, 4); nmacs++; /* re-allocate memory if needed */ if (*pc && nmacs >= workcopy->poolsize) { if (!(h = new_wormhash(nmacs * 2))) { ebt_print_memory(); } copy_wormhash(h, workcopy); free(workcopy); workcopy = h; } /* check if end of string was reached */ if (!*pc) { break; } /* now `pc' points to comma if we are here; */ /* increment this to the next char */ /* but first assert :-> */ if (*pc != ',') { ebt_print_error("Something went wrong; no comma...\n"); return NULL; } pc++; /* again check if end of string was reached; */ /* we allow an ending comma */ if (!*pc) { break; } } if (!(result = new_wormhash(nmacs))) { ebt_print_memory(); } copy_wormhash(result, workcopy); free(workcopy); qsort(&result->pool, result->poolsize, sizeof(struct ebt_mac_wormhash_tuple), fcmp); index_table(result); return result; } #define OPT_DST 0x01 #define OPT_SRC 0x02 static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_among_info *info = (struct ebt_among_info *) (*match)->data; struct ebt_mac_wormhash *wh; struct ebt_entry_match *h; int new_size; long flen = 0; int fd = -1; switch (c) { case AMONG_DST_F: case AMONG_SRC_F: case AMONG_DST: case AMONG_SRC: if (c == AMONG_DST || c == AMONG_DST_F) { ebt_check_option2(flags, OPT_DST); } else { ebt_check_option2(flags, OPT_SRC); } if (ebt_check_inverse2(optarg)) { if (c == AMONG_DST || c == AMONG_DST_F) info->bitmask |= EBT_AMONG_DST_NEG; else info->bitmask |= EBT_AMONG_SRC_NEG; } if (c == AMONG_DST_F || c == AMONG_SRC_F) { struct stat stats; if ((fd = open(optarg, O_RDONLY)) == -1) ebt_print_error("Couldn't open file '%s'", optarg); fstat(fd, &stats); flen = stats.st_size; /* use mmap because the file will probably be big */ optarg = mmap(0, flen, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (optarg == MAP_FAILED) ebt_print_error("Couldn't map file to memory"); if (optarg[flen-1] != '\n') ebt_print_error("File should end with a newline"); if (strchr(optarg, '\n') != optarg+flen-1) ebt_print_error("File should only contain one line"); optarg[flen-1] = '\0'; if (ebt_errormsg[0] != '\0') { munmap(argv, flen); close(fd); exit(-1); } } wh = create_wormhash(optarg); if (ebt_errormsg[0] != '\0') break; new_size = old_size+ebt_mac_wormhash_size(wh); h = malloc(sizeof(struct ebt_entry_match)+EBT_ALIGN(new_size)); if (!h) ebt_print_memory(); memcpy(h, *match, old_size+sizeof(struct ebt_entry_match)); memcpy((char *)h+old_size+sizeof(struct ebt_entry_match), wh, ebt_mac_wormhash_size(wh)); h->match_size = EBT_ALIGN(new_size); info = (struct ebt_among_info *) h->data; if (c == AMONG_DST || c == AMONG_DST_F) { info->wh_dst_ofs = old_size; } else { info->wh_src_ofs = old_size; } old_size = new_size; free(*match); *match = h; free(wh); if (c == AMONG_DST_F || c == AMONG_SRC_F) { munmap(argv, flen); close(fd); } break; default: return 0; } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { } #ifdef DEBUG static void wormhash_debug(const struct ebt_mac_wormhash *wh) { int i; printf("poolsize: %d\n", wh->poolsize); for (i = 0; i <= 256; i++) { printf("%02x ", wh->table[i]); if (i % 16 == 15) { printf("\n"); } } printf("\n"); } #endif /* DEBUG */ static void wormhash_printout(const struct ebt_mac_wormhash *wh) { int i; unsigned char *ip; for (i = 0; i < wh->poolsize; i++) { const struct ebt_mac_wormhash_tuple *p; p = (const struct ebt_mac_wormhash_tuple *)(&wh->pool[i]); ebt_print_mac(((const unsigned char *) &p->cmp[0]) + 2); if (p->ip) { ip = (unsigned char *) &p->ip; printf("=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } printf(","); } printf(" "); } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_among_info *info = (struct ebt_among_info *)match->data; if (info->wh_dst_ofs) { printf("--among-dst "); if (info->bitmask && EBT_AMONG_DST_NEG) { printf("! "); } wormhash_printout(ebt_among_wh_dst(info)); } if (info->wh_src_ofs) { printf("--among-src "); if (info->bitmask && EBT_AMONG_SRC_NEG) { printf("! "); } wormhash_printout(ebt_among_wh_src(info)); } } static int compare_wh(const struct ebt_mac_wormhash *aw, const struct ebt_mac_wormhash *bw) { int as, bs; as = ebt_mac_wormhash_size(aw); bs = ebt_mac_wormhash_size(bw); if (as != bs) return 0; if (as && memcmp(aw, bw, as)) return 0; return 1; } static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2) { struct ebt_among_info *a = (struct ebt_among_info *) m1->data; struct ebt_among_info *b = (struct ebt_among_info *) m2->data; if (!compare_wh(ebt_among_wh_dst(a), ebt_among_wh_dst(b))) return 0; if (!compare_wh(ebt_among_wh_src(a), ebt_among_wh_src(b))) return 0; if (a->bitmask != b->bitmask) return 0; return 1; } static struct ebt_u_match among_match = { .name = "among", .size = sizeof(struct ebt_among_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_match(&among_match); } ebtables-v2.0.10-4/extensions/ebtable_filter.c0000644000175000017500000000114411672451147021436 0ustar bdschuymbdschuym/* ebtable_filter * * Authors: * Bart De Schuymer * * April, 2002 */ #include #include "../include/ebtables_u.h" #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ (1 << NF_BR_LOCAL_OUT)) static void print_help(const char **hn) { int i; printf("Supported chains for the filter table:\n"); for (i = 0; i < NF_BR_NUMHOOKS; i++) if (FILTER_VALID_HOOKS & (1 << i)) printf("%s ", hn[i]); printf("\n"); } static struct ebt_u_table table = { .name = "filter", .help = print_help, }; void _init(void) { ebt_register_table(&table); } ebtables-v2.0.10-4/extensions/ebt_limit.c0000644000175000017500000001152711672451147020451 0ustar bdschuymbdschuym/* ebt_limit * * Authors: * Tom Marshall * * Mostly copied from iptables' limit match. * * September, 2003 */ #include #include #include #include #include #include "../include/ebtables_u.h" #include #define EBT_LIMIT_AVG "3/hour" #define EBT_LIMIT_BURST 5 static int string_to_number(const char *s, unsigned int min, unsigned int max, unsigned int *ret) { long number; char *end; errno = 0; number = strtol(s, &end, 0); if (*end == '\0' && end != s) { if (errno != ERANGE && min <= number && number <= max) { *ret = number; return 0; } } return -1; } #define FLAG_LIMIT 0x01 #define FLAG_LIMIT_BURST 0x02 #define ARG_LIMIT '1' #define ARG_LIMIT_BURST '2' static struct option opts[] = { { "limit", required_argument, 0, ARG_LIMIT }, { "limit-burst", required_argument, 0, ARG_LIMIT_BURST }, { 0 } }; static void print_help(void) { printf( "limit options:\n" "--limit avg : max average match rate: default "EBT_LIMIT_AVG"\n" " [Packets per second unless followed by \n" " /sec /minute /hour /day postfixes]\n" "--limit-burst number : number to match in a burst, -1 < number < 10001,\n" " default %u\n", EBT_LIMIT_BURST); } static int parse_rate(const char *rate, u_int32_t *val) { const char *delim; u_int32_t r; u_int32_t mult = 1; /* Seconds by default. */ delim = strchr(rate, '/'); if (delim) { if (strlen(delim+1) == 0) return 0; if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) mult = 1; else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) mult = 60; else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) mult = 60*60; else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) mult = 24*60*60; else return 0; } r = atoi(rate); if (!r) return 0; /* This would get mapped to infinite (1/day is minimum they can specify, so we're ok at that end). */ if (r / mult > EBT_LIMIT_SCALE) return 0; *val = EBT_LIMIT_SCALE * mult / r; return 1; } /* Initialize the match. */ static void init(struct ebt_entry_match *m) { struct ebt_limit_info *r = (struct ebt_limit_info *)m->data; parse_rate(EBT_LIMIT_AVG, &r->avg); r->burst = EBT_LIMIT_BURST; } /* FIXME: handle overflow: if (r->avg*r->burst/r->burst != r->avg) exit_error(PARAMETER_PROBLEM, "Sorry: burst too large for that avg rate.\n"); */ static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data; unsigned int num; switch(c) { case ARG_LIMIT: ebt_check_option2(flags, FLAG_LIMIT); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --limit"); if (!parse_rate(optarg, &r->avg)) ebt_print_error2("bad rate `%s'", optarg); break; case ARG_LIMIT_BURST: ebt_check_option2(flags, FLAG_LIMIT_BURST); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --limit-burst"); if (string_to_number(optarg, 0, 10000, &num) == -1) ebt_print_error2("bad --limit-burst `%s'", optarg); r->burst = num; break; default: return 0; } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { } struct rates { const char *name; u_int32_t mult; }; static struct rates g_rates[] = { { "day", EBT_LIMIT_SCALE*24*60*60 }, { "hour", EBT_LIMIT_SCALE*60*60 }, { "min", EBT_LIMIT_SCALE*60 }, { "sec", EBT_LIMIT_SCALE } }; static void print_rate(u_int32_t period) { unsigned int i; for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++) if (period > g_rates[i].mult || g_rates[i].mult/period < g_rates[i].mult%period) break; printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name); } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_limit_info *r = (struct ebt_limit_info *)match->data; printf("--limit "); print_rate(r->avg); printf("--limit-burst %u ", r->burst); } static int compare(const struct ebt_entry_match* m1, const struct ebt_entry_match *m2) { struct ebt_limit_info* li1 = (struct ebt_limit_info*)m1->data; struct ebt_limit_info* li2 = (struct ebt_limit_info*)m2->data; if (li1->avg != li2->avg) return 0; if (li1->burst != li2->burst) return 0; return 1; } static struct ebt_u_match limit_match = { .name = "limit", .size = sizeof(struct ebt_limit_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_match(&limit_match); } ebtables-v2.0.10-4/extensions/ebt_ulog.c0000644000175000017500000001216511672451147020300 0ustar bdschuymbdschuym/* ebt_ulog * * Authors: * Bart De Schuymer * * November, 2004 */ #define __need_time_t #define __need_suseconds_t #include #include #include #include #include "../include/ebtables_u.h" #include #include #define CP_NO_LIMIT_S "default_cprange" #define CP_NO_LIMIT_N 0 #define ULOG_PREFIX '1' #define ULOG_NLGROUP '2' #define ULOG_CPRANGE '3' #define ULOG_QTHRESHOLD '4' #define ULOG_ULOG '5' static struct option opts[] = { { "ulog-prefix" , required_argument, 0, ULOG_PREFIX }, { "ulog-nlgroup" , required_argument, 0, ULOG_NLGROUP }, { "ulog-cprange" , required_argument, 0, ULOG_CPRANGE }, { "ulog-qthreshold", required_argument, 0, ULOG_QTHRESHOLD }, { "ulog" , no_argument , 0, ULOG_ULOG }, { 0 } }; static void print_help() { printf( "ulog options:\n" "--ulog : use the default ulog parameters\n" "--ulog-prefix prefix : max %d characters (default is no prefix)\n" "--ulog-nlgroup group : 0 < group number < %d (default = %d)\n" "--ulog-cprange range : max copy range (default is " CP_NO_LIMIT_S ")\n" "--ulog-qthreshold : 0 < queueing threshold < %d (default = %d)\n", EBT_ULOG_PREFIX_LEN - 1, EBT_ULOG_MAXNLGROUPS + 1, EBT_ULOG_DEFAULT_NLGROUP + 1, EBT_ULOG_MAX_QLEN + 1, EBT_ULOG_DEFAULT_QTHRESHOLD); } static void init(struct ebt_entry_watcher *watcher) { struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data; uloginfo->prefix[0] = '\0'; uloginfo->nlgroup = EBT_ULOG_DEFAULT_NLGROUP; uloginfo->cprange = CP_NO_LIMIT_N; /* Use default netlink buffer size */ uloginfo->qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD; } #define OPT_PREFIX 0x01 #define OPT_NLGROUP 0x02 #define OPT_CPRANGE 0x04 #define OPT_QTHRESHOLD 0x08 #define OPT_ULOG 0x10 static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_watcher **watcher) { struct ebt_ulog_info *uloginfo; unsigned int i; char *end; uloginfo = (struct ebt_ulog_info *)(*watcher)->data; switch (c) { case ULOG_PREFIX: if (ebt_check_inverse2(optarg)) goto inverse_invalid; ebt_check_option2(flags, OPT_PREFIX); if (strlen(optarg) > EBT_ULOG_PREFIX_LEN - 1) ebt_print_error("Prefix too long for ulog-prefix"); strcpy(uloginfo->prefix, optarg); break; case ULOG_NLGROUP: if (ebt_check_inverse2(optarg)) goto inverse_invalid; ebt_check_option2(flags, OPT_NLGROUP); i = strtoul(optarg, &end, 10); if (*end != '\0') ebt_print_error2("Problem with ulog-nlgroup: %s", optarg); if (i < 1 || i > EBT_ULOG_MAXNLGROUPS) ebt_print_error2("the ulog-nlgroup number must be between 1 and 32"); uloginfo->nlgroup = i - 1; break; case ULOG_CPRANGE: if (ebt_check_inverse2(optarg)) goto inverse_invalid; ebt_check_option2(flags, OPT_CPRANGE); i = strtoul(optarg, &end, 10); if (*end != '\0') { if (strcasecmp(optarg, CP_NO_LIMIT_S)) ebt_print_error2("Problem with ulog-cprange: %s", optarg); i = CP_NO_LIMIT_N; } uloginfo->cprange = i; break; case ULOG_QTHRESHOLD: if (ebt_check_inverse2(optarg)) goto inverse_invalid; ebt_check_option2(flags, OPT_QTHRESHOLD); i = strtoul(optarg, &end, 10); if (*end != '\0') ebt_print_error2("Problem with ulog-qthreshold: %s", optarg); if (i > EBT_ULOG_MAX_QLEN) ebt_print_error2("ulog-qthreshold argument %d exceeds the maximum of %d", i, EBT_ULOG_MAX_QLEN); uloginfo->qthreshold = i; break; case ULOG_ULOG: if (ebt_check_inverse(optarg)) goto inverse_invalid; ebt_check_option2(flags, OPT_ULOG); break; default: return 0; } return 1; inverse_invalid: ebt_print_error("The use of '!' makes no sense for the ulog watcher"); return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watcher, const char *name, unsigned int hookmask, unsigned int time) { } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_watcher *watcher) { struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data; printf("--ulog-prefix \"%s\" --ulog-nlgroup %d --ulog-cprange ", uloginfo->prefix, uloginfo->nlgroup + 1); if (uloginfo->cprange == CP_NO_LIMIT_N) printf(CP_NO_LIMIT_S); else printf("%d", uloginfo->cprange); printf(" --ulog-qthreshold %d ", uloginfo->qthreshold); } static int compare(const struct ebt_entry_watcher *w1, const struct ebt_entry_watcher *w2) { struct ebt_ulog_info *uloginfo1 = (struct ebt_ulog_info *)w1->data; struct ebt_ulog_info *uloginfo2 = (struct ebt_ulog_info *)w2->data; if (uloginfo1->nlgroup != uloginfo2->nlgroup || uloginfo1->cprange != uloginfo2->cprange || uloginfo1->qthreshold != uloginfo2->qthreshold || strcmp(uloginfo1->prefix, uloginfo2->prefix)) return 0; return 1; } static struct ebt_u_watcher ulog_watcher = { .name = "ulog", .size = sizeof(struct ebt_ulog_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_watcher(&ulog_watcher); } ebtables-v2.0.10-4/extensions/ebt_arp.c0000644000175000017500000002267111672451147020117 0ustar bdschuymbdschuym/* ebt_arp * * Authors: * Bart De Schuymer * Tim Gardner * * April, 2002 */ #include #include #include #include #include "../include/ebtables_u.h" #include "../include/ethernetdb.h" #include #include #define ARP_OPCODE '1' #define ARP_HTYPE '2' #define ARP_PTYPE '3' #define ARP_IP_S '4' #define ARP_IP_D '5' #define ARP_MAC_S '6' #define ARP_MAC_D '7' #define ARP_GRAT '8' static struct option opts[] = { { "arp-opcode" , required_argument, 0, ARP_OPCODE }, { "arp-op" , required_argument, 0, ARP_OPCODE }, { "arp-htype" , required_argument, 0, ARP_HTYPE }, { "arp-ptype" , required_argument, 0, ARP_PTYPE }, { "arp-ip-src" , required_argument, 0, ARP_IP_S }, { "arp-ip-dst" , required_argument, 0, ARP_IP_D }, { "arp-mac-src" , required_argument, 0, ARP_MAC_S }, { "arp-mac-dst" , required_argument, 0, ARP_MAC_D }, { "arp-gratuitous", no_argument, 0, ARP_GRAT }, { 0 } }; #define NUMOPCODES 9 /* a few names */ static char *opcodes[] = { "Request", "Reply", "Request_Reverse", "Reply_Reverse", "DRARP_Request", "DRARP_Reply", "DRARP_Error", "InARP_Request", "ARP_NAK", }; static void print_help() { int i; printf( "arp options:\n" "--arp-opcode [!] opcode : ARP opcode (integer or string)\n" "--arp-htype [!] type : ARP hardware type (integer or string)\n" "--arp-ptype [!] type : ARP protocol type (hexadecimal or string)\n" "--arp-ip-src [!] address[/mask]: ARP IP source specification\n" "--arp-ip-dst [!] address[/mask]: ARP IP target specification\n" "--arp-mac-src [!] address[/mask]: ARP MAC source specification\n" "--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n" "[!] --arp-gratuitous : ARP gratuitous packet\n" " opcode strings: \n"); for (i = 0; i < NUMOPCODES; i++) printf(" %d = %s\n", i + 1, opcodes[i]); printf( " hardware type string: 1 = Ethernet\n" " protocol type string: see "_PATH_ETHERTYPES"\n"); } static void init(struct ebt_entry_match *match) { struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; arpinfo->invflags = 0; arpinfo->bitmask = 0; } #define OPT_OPCODE 0x01 #define OPT_HTYPE 0x02 #define OPT_PTYPE 0x04 #define OPT_IP_S 0x08 #define OPT_IP_D 0x10 #define OPT_MAC_S 0x20 #define OPT_MAC_D 0x40 #define OPT_GRAT 0x80 static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data; long int i; char *end; uint32_t *addr; uint32_t *mask; unsigned char *maddr; unsigned char *mmask; switch (c) { case ARP_OPCODE: ebt_check_option2(flags, OPT_OPCODE); if (ebt_check_inverse2(optarg)) arpinfo->invflags |= EBT_ARP_OPCODE; i = strtol(optarg, &end, 10); if (i < 0 || i >= (0x1 << 16) || *end !='\0') { for (i = 0; i < NUMOPCODES; i++) if (!strcasecmp(opcodes[i], optarg)) break; if (i == NUMOPCODES) ebt_print_error2("Problem with specified ARP opcode"); i++; } arpinfo->opcode = htons(i); arpinfo->bitmask |= EBT_ARP_OPCODE; break; case ARP_HTYPE: ebt_check_option2(flags, OPT_HTYPE); if (ebt_check_inverse2(optarg)) arpinfo->invflags |= EBT_ARP_HTYPE; i = strtol(optarg, &end, 10); if (i < 0 || i >= (0x1 << 16) || *end !='\0') { if (!strcasecmp("Ethernet", argv[optind - 1])) i = 1; else ebt_print_error2("Problem with specified ARP hardware type"); } arpinfo->htype = htons(i); arpinfo->bitmask |= EBT_ARP_HTYPE; break; case ARP_PTYPE: { uint16_t proto; ebt_check_option2(flags, OPT_PTYPE); if (ebt_check_inverse2(optarg)) arpinfo->invflags |= EBT_ARP_PTYPE; i = strtol(optarg, &end, 16); if (i < 0 || i >= (0x1 << 16) || *end !='\0') { struct ethertypeent *ent; ent = getethertypebyname(argv[optind - 1]); if (!ent) ebt_print_error2("Problem with specified ARP " "protocol type"); proto = ent->e_ethertype; } else proto = i; arpinfo->ptype = htons(proto); arpinfo->bitmask |= EBT_ARP_PTYPE; break; } case ARP_IP_S: case ARP_IP_D: if (c == ARP_IP_S) { ebt_check_option2(flags, OPT_IP_S); addr = &arpinfo->saddr; mask = &arpinfo->smsk; arpinfo->bitmask |= EBT_ARP_SRC_IP; } else { ebt_check_option2(flags, OPT_IP_D); addr = &arpinfo->daddr; mask = &arpinfo->dmsk; arpinfo->bitmask |= EBT_ARP_DST_IP; } if (ebt_check_inverse2(optarg)) { if (c == ARP_IP_S) arpinfo->invflags |= EBT_ARP_SRC_IP; else arpinfo->invflags |= EBT_ARP_DST_IP; } ebt_parse_ip_address(optarg, addr, mask); break; case ARP_MAC_S: case ARP_MAC_D: if (c == ARP_MAC_S) { ebt_check_option2(flags, OPT_MAC_S); maddr = arpinfo->smaddr; mmask = arpinfo->smmsk; arpinfo->bitmask |= EBT_ARP_SRC_MAC; } else { ebt_check_option2(flags, OPT_MAC_D); maddr = arpinfo->dmaddr; mmask = arpinfo->dmmsk; arpinfo->bitmask |= EBT_ARP_DST_MAC; } if (ebt_check_inverse2(optarg)) { if (c == ARP_MAC_S) arpinfo->invflags |= EBT_ARP_SRC_MAC; else arpinfo->invflags |= EBT_ARP_DST_MAC; } if (ebt_get_mac_and_mask(optarg, maddr, mmask)) ebt_print_error2("Problem with ARP MAC address argument"); break; case ARP_GRAT: ebt_check_option2(flags, OPT_GRAT); arpinfo->bitmask |= EBT_ARP_GRAT; if (ebt_invert) arpinfo->invflags |= EBT_ARP_GRAT; break; default: return 0; } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) || entry->invflags & EBT_IPROTO) ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP"); } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; int i; if (arpinfo->bitmask & EBT_ARP_OPCODE) { int opcode = ntohs(arpinfo->opcode); printf("--arp-op "); if (arpinfo->invflags & EBT_ARP_OPCODE) printf("! "); if (opcode > 0 && opcode <= NUMOPCODES) printf("%s ", opcodes[opcode - 1]); else printf("%d ", opcode); } if (arpinfo->bitmask & EBT_ARP_HTYPE) { printf("--arp-htype "); if (arpinfo->invflags & EBT_ARP_HTYPE) printf("! "); printf("%d ", ntohs(arpinfo->htype)); } if (arpinfo->bitmask & EBT_ARP_PTYPE) { struct ethertypeent *ent; printf("--arp-ptype "); if (arpinfo->invflags & EBT_ARP_PTYPE) printf("! "); ent = getethertypebynumber(ntohs(arpinfo->ptype)); if (!ent) printf("0x%x ", ntohs(arpinfo->ptype)); else printf("%s ", ent->e_name); } if (arpinfo->bitmask & EBT_ARP_SRC_IP) { printf("--arp-ip-src "); if (arpinfo->invflags & EBT_ARP_SRC_IP) printf("! "); for (i = 0; i < 4; i++) printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i], (i == 3) ? "" : "."); printf("%s ", ebt_mask_to_dotted(arpinfo->smsk)); } if (arpinfo->bitmask & EBT_ARP_DST_IP) { printf("--arp-ip-dst "); if (arpinfo->invflags & EBT_ARP_DST_IP) printf("! "); for (i = 0; i < 4; i++) printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i], (i == 3) ? "" : "."); printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk)); } if (arpinfo->bitmask & EBT_ARP_SRC_MAC) { printf("--arp-mac-src "); if (arpinfo->invflags & EBT_ARP_SRC_MAC) printf("! "); ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk); printf(" "); } if (arpinfo->bitmask & EBT_ARP_DST_MAC) { printf("--arp-mac-dst "); if (arpinfo->invflags & EBT_ARP_DST_MAC) printf("! "); ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk); printf(" "); } if (arpinfo->bitmask & EBT_ARP_GRAT) { if (arpinfo->invflags & EBT_ARP_GRAT) printf("! "); printf("--arp-gratuitous "); } } static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2) { struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data; struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data; if (arpinfo1->bitmask != arpinfo2->bitmask) return 0; if (arpinfo1->invflags != arpinfo2->invflags) return 0; if (arpinfo1->bitmask & EBT_ARP_OPCODE) { if (arpinfo1->opcode != arpinfo2->opcode) return 0; } if (arpinfo1->bitmask & EBT_ARP_HTYPE) { if (arpinfo1->htype != arpinfo2->htype) return 0; } if (arpinfo1->bitmask & EBT_ARP_PTYPE) { if (arpinfo1->ptype != arpinfo2->ptype) return 0; } if (arpinfo1->bitmask & EBT_ARP_SRC_IP) { if (arpinfo1->saddr != arpinfo2->saddr) return 0; if (arpinfo1->smsk != arpinfo2->smsk) return 0; } if (arpinfo1->bitmask & EBT_ARP_DST_IP) { if (arpinfo1->daddr != arpinfo2->daddr) return 0; if (arpinfo1->dmsk != arpinfo2->dmsk) return 0; } if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) { if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN)) return 0; if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN)) return 0; } if (arpinfo1->bitmask & EBT_ARP_DST_MAC) { if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN)) return 0; if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN)) return 0; } return 1; } static struct ebt_u_match arp_match = { .name = "arp", .size = sizeof(struct ebt_arp_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; void _init(void) { ebt_register_match(&arp_match); } ebtables-v2.0.10-4/extensions/ebt_mark.c0000644000175000017500000001246411672451147020266 0ustar bdschuymbdschuym/* ebt_mark * * Authors: * Bart De Schuymer * * July, 2002, September 2006 */ #include #include #include #include #include "../include/ebtables_u.h" #include static int mark_supplied; #define MARK_TARGET '1' #define MARK_SETMARK '2' #define MARK_ORMARK '3' #define MARK_ANDMARK '4' #define MARK_XORMARK '5' static struct option opts[] = { { "mark-target" , required_argument, 0, MARK_TARGET }, /* an oldtime messup, we should have always used the scheme * -