arptables-v0.0.3-4/0000700000175000017500000000000011345216210014215 5ustar bdschuymbdschuymarptables-v0.0.3-4/arptables.c0000600000175000017500000016736311345216210016360 0ustar bdschuymbdschuym/* Code to take an arptables-style command line and do it. */ /* * arptables: * Author: Bart De Schuymer , but * almost all code is from the iptables userspace program, which has main * authors: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au * * 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. */ /* Currently, only support for specifying hardware addresses for Ethernet is available. This tool is not luser-proof: you can specify an Ethernet source address and set hardware length to something different than 6, f.e. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef ARPT_LIB_DIR #define ARPT_LIB_DIR "/usr/local/lib/arptables" #endif #ifndef PROC_SYS_MODPROBE #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" #endif #define FMT_NUMERIC 0x0001 #define FMT_NOCOUNTS 0x0002 #define FMT_KILOMEGAGIGA 0x0004 #define FMT_OPTIONS 0x0008 #define FMT_NOTABLE 0x0010 #define FMT_NOTARGET 0x0020 #define FMT_VIA 0x0040 #define FMT_NONEWLINE 0x0080 #define FMT_LINENUMBERS 0x0100 #define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ | FMT_NUMERIC | FMT_NOTABLE) #define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (notab)) #define CMD_NONE 0x0000U #define CMD_INSERT 0x0001U #define CMD_DELETE 0x0002U #define CMD_DELETE_NUM 0x0004U #define CMD_REPLACE 0x0008U #define CMD_APPEND 0x0010U #define CMD_LIST 0x0020U #define CMD_FLUSH 0x0040U #define CMD_ZERO 0x0080U #define CMD_NEW_CHAIN 0x0100U #define CMD_DELETE_CHAIN 0x0200U #define CMD_SET_POLICY 0x0400U #define CMD_CHECK 0x0800U #define CMD_RENAME_CHAIN 0x1000U #define NUMBER_OF_CMD 13 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', 'N', 'X', 'P', 'E' }; #define OPTION_OFFSET 256 #define OPT_NONE 0x00000U #define OPT_NUMERIC 0x00001U #define OPT_S_IP 0x00002U #define OPT_D_IP 0x00004U #define OPT_S_MAC 0x00008U #define OPT_D_MAC 0x00010U #define OPT_H_LENGTH 0x00020U #define OPT_P_LENGTH 0x00040U #define OPT_OPCODE 0x00080U #define OPT_H_TYPE 0x00100U #define OPT_P_TYPE 0x00200U #define OPT_JUMP 0x00400U #define OPT_VERBOSE 0x00800U #define OPT_VIANAMEIN 0x01000U #define OPT_VIANAMEOUT 0x02000U #define OPT_LINENUMBERS 0x04000U #define OPT_COUNTERS 0x08000U #define NUMBER_OF_OPT 16 static const char optflags[NUMBER_OF_OPT] = { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'}; static struct option original_opts[] = { { "append", 1, 0, 'A' }, { "delete", 1, 0, 'D' }, { "insert", 1, 0, 'I' }, { "replace", 1, 0, 'R' }, { "list", 2, 0, 'L' }, { "flush", 2, 0, 'F' }, { "zero", 2, 0, 'Z' }, { "new-chain", 1, 0, 'N' }, { "delete-chain", 2, 0, 'X' }, { "rename-chain", 1, 0, 'E' }, { "policy", 1, 0, 'P' }, { "source-ip", 1, 0, 's' }, { "destination-ip", 1, 0, 'd' }, { "src-ip", 1, 0, 's' }, { "dst-ip", 1, 0, 'd' }, { "source-mac", 1, 0, 2}, { "destination-mac", 1, 0, 3}, { "src-mac", 1, 0, 2}, { "dst-mac", 1, 0, 3}, { "h-length", 1, 0, 'l' }, { "p-length", 1, 0, 8 }, { "opcode", 1, 0, 4 }, { "h-type", 1, 0, 5 }, { "proto-type", 1, 0, 6 }, { "in-interface", 1, 0, 'i' }, { "jump", 1, 0, 'j' }, { "table", 1, 0, 't' }, { "match", 1, 0, 'm' }, { "numeric", 0, 0, 'n' }, { "out-interface", 1, 0, 'o' }, { "verbose", 0, 0, 'v' }, { "exact", 0, 0, 'x' }, { "version", 0, 0, 'V' }, { "help", 2, 0, 'h' }, { "line-numbers", 0, 0, '0' }, { "modprobe", 1, 0, 'M' }, { 0 } }; int RUNTIME_NF_ARP_NUMHOOKS = 3; /*#ifndef __OPTIMIZE__ struct arpt_entry_target * arpt_get_target(struct arpt_entry *e) { return (void *)e + e->target_offset; } #endif*/ static struct option *opts = original_opts; static unsigned int global_option_offset = 0; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to * CMD_LIST and CMD_ZERO only). * Key: * + compulsory * x illegal * optional */ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /* Well, it's better than "Re: Linux vs FreeBSD" */ { /* -n -s -d -p -j -v -x -i -o -f --line */ /*INSERT*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*DELETE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*REPLACE*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*APPEND*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*LIST*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*FLUSH*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*ZERO*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*CHECK*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*RENAME*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '} }; static int inverse_for_options[NUMBER_OF_OPT] = { /* -n */ 0, /* -s */ ARPT_INV_SRCIP, /* -d */ ARPT_INV_TGTIP, /* 2 */ ARPT_INV_SRCDEVADDR, /* 3 */ ARPT_INV_TGTDEVADDR, /* -l */ ARPT_INV_ARPHLN, /* 4 */ ARPT_INV_ARPOP, /* 5 */ ARPT_INV_ARPHRD, /* 6 */ ARPT_INV_ARPPRO, /* -j */ 0, /* -v */ 0, /* -i */ ARPT_INV_VIA_IN, /* -o */ ARPT_INV_VIA_OUT, /*--line*/ 0, /* -c */ 0, }; const char *program_version = ARPTABLES_VERSION; const char *program_name; /* Keeping track of external matches and targets: linked lists. */ struct arptables_match *arptables_matches = NULL; struct arptables_target *arptables_targets = NULL; /* Extra debugging from libarptc */ extern void dump_entries(const arptc_handle_t handle); /* A few hardcoded protocols for 'all' and in case the user has no /etc/protocols */ struct pprot { char *name; u_int8_t num; }; /* Primitive headers... */ /* defined in netinet/in.h */ #if 0 #ifndef IPPROTO_ESP #define IPPROTO_ESP 50 #endif #ifndef IPPROTO_AH #define IPPROTO_AH 51 #endif #endif /***********************************************/ /* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */ /***********************************************/ unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0}; unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0}; unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; /* a few names */ static char *opcodes[] = { "Request", "Reply", "Request_Reverse", "Reply_Reverse", "DRARP_Request", "DRARP_Reply", "DRARP_Error", "InARP_Request", "ARP_NAK", }; #define NUMOPCODES 9 /* * put the mac address into 6 (ETH_ALEN) bytes */ int getmac_and_mask(char *from, char *to, 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 ( (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; } int getlength_and_mask(char *from, uint8_t *to, uint8_t *mask) { char *p, *buffer; int i; if ( (p = strrchr(from, '/')) != NULL) { *p = '\0'; i = strtol(p+1, &buffer, 10); if (*buffer != '\0' || i < 0 || i > 255) return -1; *mask = (uint8_t)i; } else *mask = 255; i = strtol(from, &buffer, 10); if (*buffer != '\0' || i < 0 || i > 255) return -1; *to = (uint8_t)i; return 0; } int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base) { char *p, *buffer; int i; if ( (p = strrchr(from, '/')) != NULL) { *p = '\0'; i = strtol(p+1, &buffer, base); if (*buffer != '\0' || i < 0 || i > 65535) return -1; *mask = htons((uint16_t)i); } else *mask = 65535; i = strtol(from, &buffer, base); if (*buffer != '\0' || i < 0 || i > 65535) return -1; *to = htons((uint16_t)i); return 0; } void print_mac(const unsigned char *mac, int l) { int j; for (j = 0; j < l; j++) printf("%02x%s", mac[j], (j==l-1) ? "" : ":"); } void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l) { int i; print_mac(mac, l); for (i = 0; i < l ; i++) if (mask[i] != 255) break; if (i == l) return; printf("/"); print_mac(mask, l); } /*********************************************/ /* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */ /*********************************************/ struct in_addr * dotted_to_addr(const char *dotted) { static struct in_addr addr; unsigned char *addrp; char *p, *q; unsigned int onebyte; int i; char buf[20]; /* copy dotted string, because we need to modify it */ strncpy(buf, dotted, sizeof(buf) - 1); addrp = (unsigned char *) &(addr.s_addr); p = buf; for (i = 0; i < 3; i++) { if ((q = strchr(p, '.')) == NULL) return (struct in_addr *) NULL; *q = '\0'; if (string_to_number(p, 0, 255, &onebyte) == -1) return (struct in_addr *) NULL; addrp[i] = (unsigned char) onebyte; p = q + 1; } /* we've checked 3 bytes, now we check the last one */ if (string_to_number(p, 0, 255, &onebyte) == -1) return (struct in_addr *) NULL; addrp[3] = (unsigned char) onebyte; return &addr; } static struct in_addr * network_to_addr(const char *name) { struct netent *net; static struct in_addr addr; if ((net = getnetbyname(name)) != NULL) { if (net->n_addrtype != AF_INET) return (struct in_addr *) NULL; addr.s_addr = htonl((unsigned long) net->n_net); return &addr; } return (struct in_addr *) NULL; } static void inaddrcpy(struct in_addr *dst, struct in_addr *src) { /* memcpy(dst, src, sizeof(struct in_addr)); */ dst->s_addr = src->s_addr; } void exit_error(enum exittype status, char *msg, ...) { va_list args; va_start(args, msg); fprintf(stderr, "%s v%s: ", program_name, program_version); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, "\n"); if (status == PARAMETER_PROBLEM) exit_tryhelp(status); if (status == VERSION_PROBLEM) fprintf(stderr, "Perhaps arptables or your kernel needs to be upgraded.\n"); exit(status); } void exit_tryhelp(int status) { fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", program_name, program_name ); exit(status); } void exit_printhelp(void) { struct arptables_match *m = NULL; struct arptables_target *t = NULL; int i; printf("%s v%s\n\n" "Usage: %s -[AD] chain rule-specification [options]\n" " %s -[RI] chain rulenum rule-specification [options]\n" " %s -D chain rulenum [options]\n" " %s -[LFZ] [chain] [options]\n" " %s -[NX] chain\n" " %s -E old-chain-name new-chain-name\n" " %s -P chain target [options]\n" " %s -h (print this help information)\n\n", program_name, program_version, program_name, program_name, program_name, program_name, program_name, program_name, program_name, program_name); printf( "Commands:\n" "Either long or short options are allowed.\n" " --append -A chain Append to chain\n" " --delete -D chain Delete matching rule from chain\n" " --delete -D chain rulenum\n" " Delete rule rulenum (1 = first) from chain\n" " --insert -I chain [rulenum]\n" " Insert in chain as rulenum (default 1=first)\n" " --replace -R chain rulenum\n" " Replace rule rulenum (1 = first) in chain\n" " --list -L [chain] List the rules in a chain or all chains\n" " --flush -F [chain] Delete all rules in chain or all chains\n" " --zero -Z [chain] Zero counters in chain or all chains\n" " --new -N chain Create a new user-defined chain\n" " --delete-chain\n" " -X [chain] Delete a user-defined chain\n" " --policy -P chain target\n" " Change policy on chain to target\n" " --rename-chain\n" " -E old-chain new-chain\n" " Change chain name, (moving any references)\n" "Options:\n" " --source-ip -s [!] address[/mask]\n" " source specification\n" " --destination-ip -d [!] address[/mask]\n" " destination specification\n" " --source-mac [!] address[/mask]\n" " --destination-mac [!] address[/mask]\n" " --h-length -l length[/mask] hardware length (nr of bytes)\n" " --opcode code[/mask] operation code (2 bytes)\n" " --h-type type[/mask] hardware type (2 bytes, hexadecimal)\n" " --proto-type type[/mask] protocol type (2 bytes)\n" " --in-interface -i [!] input name[+]\n" " network interface name ([+] for wildcard)\n" " --out-interface -o [!] output name[+]\n" " network interface name ([+] for wildcard)\n" " --jump -j target\n" " target for rule (may load target extension)\n" " --match -m match\n" " extended match (may load extension)\n" " --numeric -n numeric output of addresses and ports\n" " --table -t table table to manipulate (default: `filter')\n" " --verbose -v verbose mode\n" " --line-numbers print line numbers when listing\n" " --exact -x expand numbers (display exact values)\n" " --modprobe= try to insert modules using this command\n" " --set-counters PKTS BYTES set the counter during insert/append\n" "[!] --version -V print package version.\n"); printf(" 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: 0x800 = IPv4\n"); /* Print out any special helps. A user might like to be able to add a --help to the commandline, and see expected results. So we call help for all matches & targets */ for (t=arptables_targets;t;t=t->next) { printf("\n"); t->help(); } for (m=arptables_matches;m;m=m->next) { printf("\n"); m->help(); } exit(0); } static void generic_opt_check(int command, int options) { int i, j, legal = 0; /* Check that commands are valid with options. Complicated by the * fact that if an option is legal with *any* command given, it is * legal overall (ie. -z and -l). */ for (i = 0; i < NUMBER_OF_OPT; i++) { legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ for (j = 0; j < NUMBER_OF_CMD; j++) { if (!(command & (1< 1; option >>= 1, ptr++); return *ptr; } static char cmd2char(int option) { const char *ptr; for (ptr = cmdflags; option > 1; option >>= 1, ptr++); return *ptr; } static void add_command(unsigned int *cmd, const int newcmd, const unsigned int othercmds, int invert) { if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); if (*cmd & (~othercmds)) exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); *cmd |= newcmd; } int check_inverse(const char option[], int *invert, int *optind, int argc) { if (option && strcmp(option, "!") == 0) { if (*invert) exit_error(PARAMETER_PROBLEM, "Multiple `!' flags not allowed"); *invert = TRUE; if (optind) { *optind = *optind+1; if (argc && *optind > argc) exit_error(PARAMETER_PROBLEM, "no argument following `!'"); } return TRUE; } return FALSE; } static void * fw_calloc(size_t count, size_t size) { void *p; if ((p = calloc(count, size)) == NULL) { perror("arptables: calloc failed"); exit(1); } return p; } static void * fw_malloc(size_t size) { void *p; if ((p = malloc(size)) == NULL) { perror("arptables: malloc failed"); exit(1); } return p; } static struct in_addr * host_to_addr(const char *name, unsigned int *naddr) { struct hostent *host; struct in_addr *addr; unsigned int i; *naddr = 0; if ((host = gethostbyname(name)) != NULL) { if (host->h_addrtype != AF_INET || host->h_length != sizeof(struct in_addr)) return (struct in_addr *) NULL; while (host->h_addr_list[*naddr] != (char *) NULL) (*naddr)++; addr = fw_calloc(*naddr, sizeof(struct in_addr)); for (i = 0; i < *naddr; i++) inaddrcpy(&(addr[i]), (struct in_addr *) host->h_addr_list[i]); return addr; } return (struct in_addr *) NULL; } static char * addr_to_host(const struct in_addr *addr) { struct hostent *host; if ((host = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET)) != NULL) return (char *) host->h_name; return (char *) NULL; } /* * All functions starting with "parse" should succeed, otherwise * the program fails. * Most routines return pointers to static data that may change * between calls to the same or other routines with a few exceptions: * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" * return global static data. */ struct in_addr * parse_hostnetwork(const char *name, unsigned int *naddrs) { struct in_addr *addrp, *addrptmp; if ((addrptmp = dotted_to_addr(name)) != NULL || (addrptmp = network_to_addr(name)) != NULL) { addrp = fw_malloc(sizeof(struct in_addr)); inaddrcpy(addrp, addrptmp); *naddrs = 1; return addrp; } if ((addrp = host_to_addr(name, naddrs)) != NULL) return addrp; exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); } static struct in_addr * parse_mask(char *mask) { static struct in_addr maskaddr; struct in_addr *addrp; unsigned int bits; if (mask == NULL) { /* no mask at all defaults to 32 bits */ maskaddr.s_addr = 0xFFFFFFFF; return &maskaddr; } if ((addrp = dotted_to_addr(mask)) != NULL) /* dotted_to_addr already returns a network byte order addr */ return addrp; if (string_to_number(mask, 0, 32, &bits) == -1) exit_error(PARAMETER_PROBLEM, "invalid mask `%s' specified", mask); if (bits != 0) { maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); return &maskaddr; } maskaddr.s_addr = 0L; return &maskaddr; } void parse_hostnetworkmask(const char *name, struct in_addr **addrpp, struct in_addr *maskp, unsigned int *naddrs) { struct in_addr *addrp; char buf[256]; char *p; int i, j, k, n; strncpy(buf, name, sizeof(buf) - 1); if ((p = strrchr(buf, '/')) != NULL) { *p = '\0'; addrp = parse_mask(p + 1); } else addrp = parse_mask(NULL); inaddrcpy(maskp, addrp); /* if a null mask is given, the name is ignored, like in "any/0" */ if (maskp->s_addr == 0L) strcpy(buf, "0.0.0.0"); addrp = *addrpp = parse_hostnetwork(buf, naddrs); n = *naddrs; for (i = 0, j = 0; i < n; i++) { addrp[j++].s_addr &= maskp->s_addr; for (k = 0; k < j - 1; k++) { if (addrp[k].s_addr == addrp[j - 1].s_addr) { (*naddrs)--; j--; break; } } } } struct arptables_match * find_match(const char *name, enum arpt_tryload tryload) { struct arptables_match *ptr; for (ptr = arptables_matches; ptr; ptr = ptr->next) { if (strcmp(name, ptr->name) == 0) break; } if (ptr && !ptr->loaded) { if (tryload != DONT_LOAD) ptr->loaded = 1; else ptr = NULL; } if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { exit_error(PARAMETER_PROBLEM, "Couldn't find match `%s'\n", name); } if (ptr) ptr->used = 1; return ptr; } static void parse_interface(const char *arg, char *vianame, unsigned char *mask) { int vialen = strlen(arg); unsigned int i; memset(mask, 0, IFNAMSIZ); memset(vianame, 0, IFNAMSIZ); if (vialen + 1 > IFNAMSIZ) exit_error(PARAMETER_PROBLEM, "interface name `%s' must be shorter than IFNAMSIZ" " (%i)", arg, IFNAMSIZ-1); strcpy(vianame, arg); if (vialen == 0) memset(mask, 0, IFNAMSIZ); else if (vianame[vialen - 1] == '+') { memset(mask, 0xFF, vialen - 1); memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); /* Don't remove `+' here! -HW */ } else { /* Include nul-terminator in match */ memset(mask, 0xFF, vialen + 1); memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); for (i = 0; vianame[i]; i++) { if (!isalnum(vianame[i]) && vianame[i] != '_' && vianame[i] != '.') { printf("Warning: wierd character in interface" " `%s' (No aliases, :, ! or *).\n", vianame); break; } } } } /* Can't be zero. */ static int parse_rulenumber(const char *rule) { unsigned int rulenum; if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) exit_error(PARAMETER_PROBLEM, "Invalid rule number `%s'", rule); return rulenum; } static const char * parse_target(const char *targetname) { const char *ptr; if (strlen(targetname) < 1) exit_error(PARAMETER_PROBLEM, "Invalid target name (too short)"); if (strlen(targetname)+1 > sizeof(arpt_chainlabel)) exit_error(PARAMETER_PROBLEM, "Invalid target name `%s' (%i chars max)", targetname, sizeof(arpt_chainlabel)-1); for (ptr = targetname; *ptr; ptr++) if (isspace(*ptr)) exit_error(PARAMETER_PROBLEM, "Invalid target name `%s'", targetname); return targetname; } static char * addr_to_network(const struct in_addr *addr) { struct netent *net; if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) return (char *) net->n_name; return (char *) NULL; } char * addr_to_dotted(const struct in_addr *addrp) { static char buf[20]; const unsigned char *bytep; bytep = (const unsigned char *) &(addrp->s_addr); sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); return buf; } char * addr_to_anyname(const struct in_addr *addr) { char *name; if ((name = addr_to_host(addr)) != NULL || (name = addr_to_network(addr)) != NULL) return name; return addr_to_dotted(addr); } char * mask_to_dotted(const struct in_addr *mask) { int i; static char buf[20]; u_int32_t maskaddr, bits; maskaddr = ntohl(mask->s_addr); if (maskaddr == 0xFFFFFFFFL) /* we don't want to see "/32" */ return ""; i = 32; bits = 0xFFFFFFFEL; while (--i >= 0 && maskaddr != bits) bits <<= 1; if (i >= 0) sprintf(buf, "/%d", i); else /* mask was not a decent combination of 1's and 0's */ sprintf(buf, "/%s", addr_to_dotted(mask)); return buf; } int string_to_number(const char *s, unsigned int min, unsigned int max, unsigned int *ret) { long number; char *end; /* Handle hex, octal, etc. */ errno = 0; number = strtol(s, &end, 0); if (*end == '\0' && end != s) { /* we parsed a number, let's see if we want this */ if (errno != ERANGE && min <= number && number <= max) { *ret = number; return 0; } } return -1; } static void set_option(unsigned int *options, unsigned int option, u_int16_t *invflg, int invert) { if (*options & option) exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", opt2char(option)); *options |= option; if (invert) { unsigned int i; for (i = 0; 1 << i != option; i++); if (!inverse_for_options[i]) exit_error(PARAMETER_PROBLEM, "cannot have ! before -%c", opt2char(option)); *invflg |= inverse_for_options[i]; } } struct arptables_target * find_target(const char *name, enum arpt_tryload tryload) { struct arptables_target *ptr; /* Standard target? */ if (strcmp(name, "") == 0 || strcmp(name, ARPTC_LABEL_ACCEPT) == 0 || strcmp(name, ARPTC_LABEL_DROP) == 0 || strcmp(name, ARPTC_LABEL_QUEUE) == 0 || strcmp(name, ARPTC_LABEL_RETURN) == 0) name = "standard"; for (ptr = arptables_targets; ptr; ptr = ptr->next) { if (strcmp(name, ptr->name) == 0) break; } if (ptr && !ptr->loaded) { if (tryload != DONT_LOAD) ptr->loaded = 1; else ptr = NULL; } if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { exit_error(PARAMETER_PROBLEM, "Couldn't find target `%s'\n", name); } if (ptr) ptr->used = 1; return ptr; } static struct option * merge_options(struct option *oldopts, const struct option *newopts, unsigned int *option_offset) { unsigned int num_old, num_new, i; struct option *merge; 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; *option_offset = global_option_offset; merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); 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 += *option_offset; } memset(merge + num_old + num_new, 0, sizeof(struct option)); return merge; } void register_match(struct arptables_match *me) { struct arptables_match **i; if (strcmp(me->version, program_version) != 0) { fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", program_name, me->name, me->version, program_version); exit(1); } if (find_match(me->name, DONT_LOAD)) { fprintf(stderr, "%s: match `%s' already registered.\n", program_name, me->name); exit(1); } if (me->size != ARPT_ALIGN(me->size)) { fprintf(stderr, "%s: match `%s' has invalid size %u.\n", program_name, me->name, me->size); exit(1); } /* Append to list. */ for (i = &arptables_matches; *i; i = &(*i)->next); me->next = NULL; *i = me; me->m = NULL; me->mflags = 0; } void register_target(struct arptables_target *me) { if (strcmp(me->version, program_version) != 0) { fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", program_name, me->name, me->version, program_version); exit(1); } if (find_target(me->name, DONT_LOAD)) { fprintf(stderr, "%s: target `%s' already registered.\n", program_name, me->name); exit(1); } if (me->size != ARPT_ALIGN(me->size)) { fprintf(stderr, "%s: target `%s' has invalid size %u.\n", program_name, me->name, me->size); exit(1); } /* Prepend to list. */ me->next = arptables_targets; arptables_targets = me; me->t = NULL; me->tflags = 0; } static void print_num(u_int64_t number, unsigned int format) { if (format & FMT_KILOMEGAGIGA) { if (number > 99999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; if (number > 9999) { number = (number + 500) / 1000; printf(FMT("%4lluT ","%lluT "), number); } else printf(FMT("%4lluG ","%lluG "), number); } else printf(FMT("%4lluM ","%lluM "), number); } else printf(FMT("%4lluK ","%lluK "), number); } else printf(FMT("%5llu ","%llu "), number); } else printf(FMT("%8llu ","%llu "), number); } static void print_header(unsigned int format, const char *chain, arptc_handle_t *handle) { struct arpt_counters counters; const char *pol = arptc_get_policy(chain, &counters, handle); printf("Chain %s", chain); if (pol) { printf(" (policy %s", pol); if (!(format & FMT_NOCOUNTS)) { fputc(' ', stdout); print_num(counters.pcnt, (format|FMT_NOTABLE)); fputs("packets, ", stdout); print_num(counters.bcnt, (format|FMT_NOTABLE)); fputs("bytes", stdout); } printf(")\n"); } else { unsigned int refs; if (!arptc_get_references(&refs, chain, handle)) printf(" (ERROR obtaining refs)\n"); else printf(" (%u references)\n", refs); } /* I don't like this if (format & FMT_LINENUMBERS) printf(FMT("%-4s ", "%s "), "num"); if (!(format & FMT_NOCOUNTS)) { if (format & FMT_KILOMEGAGIGA) { printf(FMT("%5s ","%s "), "pkts"); printf(FMT("%5s ","%s "), "bytes"); } else { printf(FMT("%8s ","%s "), "pkts"); printf(FMT("%10s ","%s "), "bytes"); } } if (!(format & FMT_NOTARGET)) printf(FMT("%-9s ","%s "), "target"); fputs(" prot ", stdout); if (format & FMT_OPTIONS) fputs("opt", stdout); if (format & FMT_VIA) { printf(FMT(" %-6s ","%s "), "in"); printf(FMT("%-6s ","%s "), "out"); } printf(FMT(" %-19s ","%s "), "source"); printf(FMT(" %-19s "," %s "), "destination"); printf("\n"); */ } /* static int print_match(const struct arpt_entry_match *m, const struct arpt_arp *arp, int numeric) { struct arptables_match *match = find_match(m->u.user.name, TRY_LOAD); if (match) { if (match->print) match->print(arp, m, numeric); else printf("%s ", match->name); } else { if (m->u.user.name[0]) printf("UNKNOWN match `%s' ", m->u.user.name); } */ /* Don't stop iterating. */ /* return 0; } */ /* e is called `fw' here for hysterical raisins */ static void print_firewall(const struct arpt_entry *fw, const char *targname, unsigned int num, unsigned int format, const arptc_handle_t handle) { struct arptables_target *target = NULL; const struct arpt_entry_target *t; u_int8_t flags; char buf[BUFSIZ]; int i; if (!arptc_is_chain(targname, handle)) target = find_target(targname, TRY_LOAD); else target = find_target(ARPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); t = arpt_get_target((struct arpt_entry *)fw); flags = fw->arp.flags; if (format & FMT_LINENUMBERS) printf("%u ", num+1); if (!(format & FMT_NOTARGET) && targname[0] != '\0') printf("-j %s ", targname); if (format & FMT_VIA) { char iface[IFNAMSIZ+2]; if (fw->arp.invflags & ARPT_INV_VIA_IN) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; if (fw->arp.iniface[0] != '\0') { strcat(iface, fw->arp.iniface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf("-i %s ", iface); if (fw->arp.invflags & ARPT_INV_VIA_OUT) { iface[0] = '!'; iface[1] = '\0'; } else iface[0] = '\0'; if (fw->arp.outiface[0] != '\0') { strcat(iface, fw->arp.outiface); } else if (format & FMT_NUMERIC) strcat(iface, "*"); else strcat(iface, "any"); printf("-o %s ", iface); } if (fw->arp.smsk.s_addr != 0L) { printf("%s", fw->arp.invflags & ARPT_INV_SRCIP ? "! " : ""); if (format & FMT_NUMERIC) sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src))); else sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src))); strcat(buf, mask_to_dotted(&(fw->arp.smsk))); printf("-s %s ", buf); } for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++) if (fw->arp.src_devaddr.mask[i] != 0) break; if (i == ARPT_DEV_ADDR_LEN_MAX) goto after_devsrc; printf("%s", fw->arp.invflags & ARPT_INV_SRCDEVADDR ? "! " : ""); printf("--src-mac "); print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr, (unsigned char *)fw->arp.src_devaddr.mask, ETH_ALEN); printf(" "); after_devsrc: if (fw->arp.tmsk.s_addr != 0L) { printf("%s",fw->arp.invflags & ARPT_INV_TGTIP ? "! " : ""); if (format & FMT_NUMERIC) sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt))); else sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt))); strcat(buf, mask_to_dotted(&(fw->arp.tmsk))); printf("-d %s ", buf); } for (i = 0; i arp.tgt_devaddr.mask[i] != 0) break; if (i == ARPT_DEV_ADDR_LEN_MAX) goto after_devdst; printf("%s",fw->arp.invflags & ARPT_INV_TGTDEVADDR ? "! " : ""); printf("--dst-mac "); print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr, (unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN); printf(" "); after_devdst: if (fw->arp.arhln_mask != 0) { printf("%s",fw->arp.invflags & ARPT_INV_ARPHLN ? "! " : ""); printf("--h-length %d", fw->arp.arhln); if (fw->arp.arhln_mask != 255) printf("/%d", fw->arp.arhln_mask); printf(" "); } if (fw->arp.arpop_mask != 0) { int tmp = ntohs(fw->arp.arpop); printf("%s",fw->arp.invflags & ARPT_INV_ARPOP ? "! " : ""); if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC)) printf("--opcode %s", opcodes[tmp-1]); else printf("--opcode %d", tmp); if (fw->arp.arpop_mask != 65535) printf("/%d", ntohs(fw->arp.arpop_mask)); printf(" "); } if (fw->arp.arhrd_mask != 0) { uint16_t tmp = ntohs(fw->arp.arhrd); printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD ? "! " : ""); if (tmp == 1 && !(format & FMT_NUMERIC)) printf("--h-type %s", "Ethernet"); else printf("--h-type %u", tmp); if (fw->arp.arhrd_mask != 65535) printf("/%d", ntohs(fw->arp.arhrd_mask)); printf(" "); } if (fw->arp.arpro_mask != 0) { int tmp = ntohs(fw->arp.arpro); printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO ? "! " : ""); if (tmp == 0x0800 && !(format & FMT_NUMERIC)) printf("--proto-type %s", "IPv4"); else printf("--proto-type 0x%x", tmp); if (fw->arp.arpro_mask != 65535) printf("/%x", ntohs(fw->arp.arpro_mask)); printf(" "); } /* FIXME ARPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); */ if (target) { if (target->print) /* Print the target information. */ target->print(&fw->arp, t, format & FMT_NUMERIC); } else if (t->u.target_size != sizeof(*t)) printf("[%u bytes of unknown target data] ", t->u.target_size - sizeof(*t)); if (!(format & FMT_NOCOUNTS)) { printf(", pcnt="); print_num(fw->counters.pcnt, format); printf("-- bcnt="); print_num(fw->counters.bcnt, format); } if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); } static void print_firewall_line(const struct arpt_entry *fw, const arptc_handle_t h) { struct arpt_entry_target *t; t = arpt_get_target((struct arpt_entry *)fw); print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); } static int append_entry(const arpt_chainlabel chain, struct arpt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], unsigned int ndaddrs, const struct in_addr daddrs[], int verbose, arptc_handle_t *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->arp.src.s_addr = saddrs[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->arp.tgt.s_addr = daddrs[j].s_addr; if (verbose) print_firewall_line(fw, *handle); ret &= arptc_append_entry(chain, fw, handle); } } return ret; } static int replace_entry(const arpt_chainlabel chain, struct arpt_entry *fw, unsigned int rulenum, const struct in_addr *saddr, const struct in_addr *daddr, int verbose, arptc_handle_t *handle) { fw->arp.src.s_addr = saddr->s_addr; fw->arp.tgt.s_addr = daddr->s_addr; if (verbose) print_firewall_line(fw, *handle); return arptc_replace_entry(chain, fw, rulenum, handle); } static int insert_entry(const arpt_chainlabel chain, struct arpt_entry *fw, unsigned int rulenum, unsigned int nsaddrs, const struct in_addr saddrs[], unsigned int ndaddrs, const struct in_addr daddrs[], int verbose, arptc_handle_t *handle) { unsigned int i, j; int ret = 1; for (i = 0; i < nsaddrs; i++) { fw->arp.src.s_addr = saddrs[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->arp.tgt.s_addr = daddrs[j].s_addr; if (verbose) print_firewall_line(fw, *handle); ret &= arptc_insert_entry(chain, fw, rulenum, handle); } } return ret; } static unsigned char * make_delete_mask(struct arpt_entry *fw) { /* Establish mask for comparison */ unsigned int size; struct arptables_match *m; unsigned char *mask, *mptr; size = sizeof(struct arpt_entry); for (m = arptables_matches; m; m = m->next) { if (!m->used) continue; size += ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size; } mask = fw_calloc(1, size + ARPT_ALIGN(sizeof(struct arpt_entry_target)) + arptables_targets->size); memset(mask, 0xFF, sizeof(struct arpt_entry)); mptr = mask + sizeof(struct arpt_entry); for (m = arptables_matches; m; m = m->next) { if (!m->used) continue; memset(mptr, 0xFF, ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->userspacesize); mptr += ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size; } memset(mptr, 0xFF, ARPT_ALIGN(sizeof(struct arpt_entry_target)) + arptables_targets->userspacesize); return mask; } static int delete_entry(const arpt_chainlabel chain, struct arpt_entry *fw, unsigned int nsaddrs, const struct in_addr saddrs[], unsigned int ndaddrs, const struct in_addr daddrs[], int verbose, arptc_handle_t *handle) { unsigned int i, j; int ret = 1; unsigned char *mask; mask = make_delete_mask(fw); for (i = 0; i < nsaddrs; i++) { fw->arp.src.s_addr = saddrs[i].s_addr; for (j = 0; j < ndaddrs; j++) { fw->arp.tgt.s_addr = daddrs[j].s_addr; if (verbose) print_firewall_line(fw, *handle); ret &= arptc_delete_entry(chain, fw, mask, handle); } } return ret; } int for_each_chain(int (*fn)(const arpt_chainlabel, int, arptc_handle_t *), int verbose, int builtinstoo, arptc_handle_t *handle) { int ret = 1; const char *chain; char *chains; unsigned int i, chaincount = 0; chain = arptc_first_chain(handle); while (chain) { chaincount++; chain = arptc_next_chain(handle); } chains = fw_malloc(sizeof(arpt_chainlabel) * chaincount); i = 0; chain = arptc_first_chain(handle); while (chain) { strcpy(chains + i*sizeof(arpt_chainlabel), chain); i++; chain = arptc_next_chain(handle); } for (i = 0; i < chaincount; i++) { if (!builtinstoo && arptc_builtin(chains + i*sizeof(arpt_chainlabel), *handle)) continue; ret &= fn(chains + i*sizeof(arpt_chainlabel), verbose, handle); } free(chains); return ret; } int flush_entries(const arpt_chainlabel chain, int verbose, arptc_handle_t *handle) { if (!chain) return for_each_chain(flush_entries, verbose, 1, handle); if (verbose) fprintf(stdout, "Flushing chain `%s'\n", chain); return arptc_flush_entries(chain, handle); } static int zero_entries(const arpt_chainlabel chain, int verbose, arptc_handle_t *handle) { if (!chain) return for_each_chain(zero_entries, verbose, 1, handle); if (verbose) fprintf(stdout, "Zeroing chain `%s'\n", chain); return arptc_zero_entries(chain, handle); } int delete_chain(const arpt_chainlabel chain, int verbose, arptc_handle_t *handle) { if (!chain) return for_each_chain(delete_chain, verbose, 0, handle); if (verbose) fprintf(stdout, "Deleting chain `%s'\n", chain); return arptc_delete_chain(chain, handle); } static int list_entries(const arpt_chainlabel chain, int verbose, int numeric, int expanded, int linenumbers, arptc_handle_t *handle) { int found = 0; unsigned int format; const char *this; format = FMT_OPTIONS; if (!verbose) format |= FMT_NOCOUNTS; else format |= FMT_VIA; if (numeric) format |= FMT_NUMERIC; if (!expanded) format |= FMT_KILOMEGAGIGA; if (linenumbers) format |= FMT_LINENUMBERS; for (this = arptc_first_chain(handle); this; this = arptc_next_chain(handle)) { const struct arpt_entry *i; unsigned int num; if (chain && strcmp(chain, this) != 0) continue; if (found) printf("\n"); print_header(format, this, handle); i = arptc_first_rule(this, handle); num = 0; while (i) { print_firewall(i, arptc_get_target(i, handle), num++, format, *handle); i = arptc_next_rule(i, handle); } found = 1; } errno = ENOENT; return found; } 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) { switch (read(procfile, ret, 1024)) { case -1: goto fail; case 1024: goto fail; /* Partial read. Wierd */ } if (ret[strlen(ret)-1]=='\n') ret[strlen(ret)-1]=0; close(procfile); return ret; } fail: free(ret); close(procfile); return NULL; } int arptables_insmod(const char *modname, const char *modprobe) { char *buf = NULL; char *argv[3]; /* If they don't explicitly set it, read out of kernel */ if (!modprobe) { buf = get_modprobe(); if (!buf) return -1; modprobe = buf; } switch (fork()) { case 0: argv[0] = (char *)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); } free(buf); return 0; } static struct arpt_entry * generate_entry(const struct arpt_entry *fw, struct arptables_match *matches, struct arpt_entry_target *target) { unsigned int size; /* struct arptables_match *m; */ struct arpt_entry *e; size = sizeof(struct arpt_entry); /* FIXME for (m = matches; m; m = m->next) { if (!m->used) continue; size += m->m->u.match_size; } */ e = fw_malloc(size + target->u.target_size); *e = *fw; e->target_offset = size; e->next_offset = size + target->u.target_size; size = 0; /* FIXME for (m = matches; m; m = m->next) { if (!m->used) continue; memcpy(e->elems + size, m->m, m->m->u.match_size); size += m->m->u.match_size; } */ memcpy(e->elems + size, target, target->u.target_size); return e; } int do_command(int argc, char *argv[], char **table, arptc_handle_t *handle) { struct arpt_entry fw, *e = NULL; int invert = 0; unsigned int nsaddrs = 0, ndaddrs = 0; struct in_addr *saddrs = NULL, *daddrs = NULL; int c, verbose = 0; const char *chain = NULL; const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; const char *policy = NULL, *newname = NULL; unsigned int rulenum = 0, options = 0, command = 0; const char *pcnt = NULL, *bcnt = NULL; int ret = 1; /* struct arptables_match *m;*/ struct arptables_target *target = NULL; struct arptables_target *t; const char *jumpto = ""; char *protocol = NULL; const char *modprobe = NULL; /* first figure out if this is a 2.6 or a 2.4 kernel */ *handle = arptc_init(*table); if (!*handle) { arptables_insmod("arp_tables", modprobe); *handle = arptc_init(*table); if (!*handle) { RUNTIME_NF_ARP_NUMHOOKS = 2; *handle = arptc_init(*table); if (!*handle) { exit_error(VERSION_PROBLEM, "can't initialize arptables table `%s': %s", *table, arptc_strerror(errno)); } } } memset(&fw, 0, sizeof(fw)); opts = original_opts; global_option_offset = 0; /* re-set optind to 0 in case do_command gets called * a second time */ optind = 0; /* clear mflags in case do_command gets called a second time * (we clear the global list of all matches for security)*/ /* for (m = arptables_matches; m; m = m->next) { m->mflags = 0; m->used = 0; }*/ for (t = arptables_targets; t; t = t->next) { t->tflags = 0; t->used = 0; } /* Suppress error messages: we may add new options if we demand-load a protocol. */ opterr = 0; while ((c = getopt_long(argc, argv, "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:l:i:vnt:m:c:", opts, NULL)) != -1) { switch (c) { /* * Command selection */ case 'A': add_command(&command, CMD_APPEND, CMD_NONE, invert); chain = optarg; break; case 'D': add_command(&command, CMD_DELETE, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') { rulenum = parse_rulenumber(argv[optind++]); command = CMD_DELETE_NUM; } break; case 'R': add_command(&command, CMD_REPLACE, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); else exit_error(PARAMETER_PROBLEM, "-%c requires a rule number", cmd2char(CMD_REPLACE)); break; case 'I': add_command(&command, CMD_INSERT, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); else rulenum = 1; break; case 'L': add_command(&command, CMD_LIST, CMD_ZERO, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'F': add_command(&command, CMD_FLUSH, CMD_NONE, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'Z': add_command(&command, CMD_ZERO, CMD_LIST, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'N': if (optarg && *optarg == '-') exit_error(PARAMETER_PROBLEM, "chain name not allowed to start " "with `-'\n"); if (find_target(optarg, TRY_LOAD)) exit_error(PARAMETER_PROBLEM, "chain name may not clash " "with target name\n"); add_command(&command, CMD_NEW_CHAIN, CMD_NONE, invert); chain = optarg; break; case 'X': add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'E': add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') newname = argv[optind++]; else exit_error(PARAMETER_PROBLEM, "-%c requires old-chain-name and " "new-chain-name", cmd2char(CMD_RENAME_CHAIN)); break; case 'P': add_command(&command, CMD_SET_POLICY, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') policy = argv[optind++]; else exit_error(PARAMETER_PROBLEM, "-%c requires a chain and a policy", cmd2char(CMD_SET_POLICY)); break; case 'h': if (!optarg) optarg = argv[optind]; /* arptables -p icmp -h */ if (!arptables_matches && protocol) find_match(protocol, TRY_LOAD); exit_printhelp(); case 's': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_S_IP, &fw.arp.invflags, invert); shostnetworkmask = argv[optind-1]; break; case 'd': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_D_IP, &fw.arp.invflags, invert); dhostnetworkmask = argv[optind-1]; break; case 2:/* src-mac */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_S_MAC, &fw.arp.invflags, invert); if (getmac_and_mask(argv[optind - 1], fw.arp.src_devaddr.addr, fw.arp.src_devaddr.mask)) exit_error(PARAMETER_PROBLEM, "Problem with specified " "source mac"); break; case 3:/* dst-mac */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_D_MAC, &fw.arp.invflags, invert); if (getmac_and_mask(argv[optind - 1], fw.arp.tgt_devaddr.addr, fw.arp.tgt_devaddr.mask)) exit_error(PARAMETER_PROBLEM, "Problem with specified " "destination mac"); break; case 'l':/* hardware length */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_H_LENGTH, &fw.arp.invflags, invert); getlength_and_mask(argv[optind - 1], &fw.arp.arhln, &fw.arp.arhln_mask); break; case 8:/* protocol length */ exit_error(PARAMETER_PROBLEM, "not supported"); /* check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_P_LENGTH, &fw.arp.invflags, invert); getlength_and_mask(argv[optind - 1], &fw.arp.arpln, &fw.arp.arpln_mask); break; */ case 4:/* opcode */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_OPCODE, &fw.arp.invflags, invert); if (get16_and_mask(argv[optind - 1], &fw.arp.arpop, &fw.arp.arpop_mask, 10)) { int i; for (i = 0; i < NUMOPCODES; i++) if (!strcasecmp(opcodes[i], optarg)) break; if (i == NUMOPCODES) exit_error(PARAMETER_PROBLEM, "Problem with specified opcode"); fw.arp.arpop = htons(i+1); } break; case 5:/* h-type */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_H_TYPE, &fw.arp.invflags, invert); if (get16_and_mask(argv[optind - 1], &fw.arp.arhrd, &fw.arp.arhrd_mask, 16)) { if (strcasecmp(argv[optind-1], "Ethernet")) exit_error(PARAMETER_PROBLEM, "Problem with specified hardware type"); fw.arp.arhrd = htons(1); } break; case 6:/* proto-type */ check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_P_TYPE, &fw.arp.invflags, invert); if (get16_and_mask(argv[optind - 1], &fw.arp.arpro, &fw.arp.arpro_mask, 10)) { if (strcasecmp(argv[optind-1], "ipv4")) exit_error(PARAMETER_PROBLEM, "Problem with specified protocol type"); fw.arp.arpro = htons(0x800); } break; case 'j': set_option(&options, OPT_JUMP, &fw.arp.invflags, invert); jumpto = parse_target(optarg); /* TRY_LOAD (may be chain name) */ target = find_target(jumpto, TRY_LOAD); if (target) { size_t size; size = ARPT_ALIGN(sizeof(struct arpt_entry_target)) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); /* target->init(target->t, &fw.nfcache); */ target->init(target->t); opts = merge_options(opts, target->extra_opts, &target->option_offset); } break; case 'i': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_VIANAMEIN, &fw.arp.invflags, invert); parse_interface(argv[optind-1], fw.arp.iniface, fw.arp.iniface_mask); /* fw.nfcache |= NFC_IP_IF_IN; */ break; case 'o': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_VIANAMEOUT, &fw.arp.invflags, invert); parse_interface(argv[optind-1], fw.arp.outiface, fw.arp.outiface_mask); /* fw.nfcache |= NFC_IP_IF_OUT; */ break; case 'v': if (!verbose) set_option(&options, OPT_VERBOSE, &fw.arp.invflags, invert); verbose++; break; case 'm': /*{ size_t size; if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); m = find_match(optarg, LOAD_MUST_SUCCEED); size = ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size; m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); m->init(m->m, &fw.nfcache); opts = merge_options(opts, m->extra_opts, &m->option_offset); }*/ break; case 'n': set_option(&options, OPT_NUMERIC, &fw.arp.invflags, invert); break; case 't': if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); *table = argv[optind-1]; break; case 'V': if (invert) printf("Not %s ;-)\n", program_version); else printf("%s v%s\n", program_name, program_version); exit(0); case '0': set_option(&options, OPT_LINENUMBERS, &fw.arp.invflags, invert); break; case 'M': modprobe = optarg; break; case 'c': set_option(&options, OPT_COUNTERS, &fw.arp.invflags, invert); pcnt = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') bcnt = argv[optind++]; else exit_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); break; case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { if (invert) exit_error(PARAMETER_PROBLEM, "multiple consecutive ! not" " allowed"); invert = TRUE; optarg[0] = '\0'; continue; } printf("Bad argument `%s'\n", optarg); exit_tryhelp(2); default: /* FIXME: This scheme doesn't allow two of the same matches --RR */ if (!target || !(target->parse(c - target->option_offset, argv, invert, &target->tflags, &fw, &target->t))) { /* for (m = arptables_matches; m; m = m->next) { if (!m->used) continue; if (m->parse(c - m->option_offset, argv, invert, &m->mflags, &fw, &fw.nfcache, &m->m)) break; } */ /* If you listen carefully, you can actually hear this code suck. */ /* some explanations (after four different bugs * in 3 different releases): If we encountere a * parameter, that has not been parsed yet, * it's not an option of an explicitly loaded * match or a target. However, we support * implicit loading of the protocol match * extension. '-p tcp' means 'l4 proto 6' and * at the same time 'load tcp protocol match on * demand if we specify --dport'. * * To make this work, we need to make sure: * - the parameter has not been parsed by * a match (m above) * - a protocol has been specified * - the protocol extension has not been * loaded yet, or is loaded and unused * [think of arptables-restore!] * - the protocol extension can be successively * loaded */ /* if (m == NULL && protocol && (!find_proto(protocol, DONT_LOAD, options&OPT_NUMERIC) || (find_proto(protocol, DONT_LOAD, options&OPT_NUMERIC) && (proto_used == 0)) ) && (m = find_proto(protocol, TRY_LOAD, options&OPT_NUMERIC))) { Try loading protocol */ /* size_t size; proto_used = 1; size = ARPT_ALIGN(sizeof(struct arpt_entry_match)) + m->size; m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); m->init(m->m, &fw.nfcache); opts = merge_options(opts, m->extra_opts, &m->option_offset); optind--; continue; } if (!m) exit_error(PARAMETER_PROBLEM, "Unknown arg `%s'", argv[optind-1]); */ } } invert = FALSE; } /* for (m = arptables_matches; m; m = m->next) { if (!m->used) continue; m->final_check(m->mflags); } */ if (target) target->final_check(target->tflags); /* Fix me: must put inverse options checking here --MN */ if (optind < argc) exit_error(PARAMETER_PROBLEM, "unknown arguments found on commandline"); if (!command) exit_error(PARAMETER_PROBLEM, "no command specified"); if (invert) exit_error(PARAMETER_PROBLEM, "nothing appropriate following !"); if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { if (!(options & OPT_D_IP)) dhostnetworkmask = "0.0.0.0/0"; if (!(options & OPT_S_IP)) shostnetworkmask = "0.0.0.0/0"; } if (shostnetworkmask) parse_hostnetworkmask(shostnetworkmask, &saddrs, &(fw.arp.smsk), &nsaddrs); if (dhostnetworkmask) parse_hostnetworkmask(dhostnetworkmask, &daddrs, &(fw.arp.tmsk), &ndaddrs); if ((nsaddrs > 1 || ndaddrs > 1) && (fw.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP))) exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" " source or destination IP addresses"); if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) exit_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); generic_opt_check(command, options); if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN) exit_error(PARAMETER_PROBLEM, "chain name `%s' too long (must be under %i chars)", chain, ARPT_FUNCTION_MAXNAMELEN); /* only allocate handle if we weren't called with a handle */ if (!*handle) *handle = arptc_init(*table); if (!*handle) { /* try to insmod the module if arptc_init failed */ arptables_insmod("arp_tables", modprobe); *handle = arptc_init(*table); } if (!*handle) exit_error(VERSION_PROBLEM, "can't initialize arptables table `%s': %s", *table, arptc_strerror(errno)); if (command == CMD_APPEND || command == CMD_DELETE || command == CMD_INSERT || command == CMD_REPLACE) { if (strcmp(chain, "PREROUTING") == 0 || strcmp(chain, "INPUT") == 0) { /* -o not valid with incoming packets. */ if (options & OPT_VIANAMEOUT) exit_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEOUT), chain); } if (strcmp(chain, "POSTROUTING") == 0 || strcmp(chain, "OUTPUT") == 0) { /* -i not valid with outgoing packets */ if (options & OPT_VIANAMEIN) exit_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEIN), chain); } if (target && arptc_is_chain(jumpto, *handle)) { printf("Warning: using chain %s, not extension\n", jumpto); target = NULL; } /* If they didn't specify a target, or it's a chain name, use standard. */ if (!target && (strlen(jumpto) == 0 || arptc_is_chain(jumpto, *handle))) { size_t size; target = find_target(ARPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); size = sizeof(struct arpt_entry_target) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); target->init(target->t); } if (!target) { /* it is no chain, and we can't load a plugin. * We cannot know if the plugin is corrupt, non * existant OR if the user just misspelled a * chain. */ find_target(jumpto, LOAD_MUST_SUCCEED); } else { e = generate_entry(&fw, arptables_matches, target->t); } } switch (command) { case CMD_APPEND: ret = append_entry(chain, e, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_DELETE: ret = delete_entry(chain, e, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_DELETE_NUM: ret = arptc_delete_num_entry(chain, rulenum - 1, handle); break; case CMD_REPLACE: ret = replace_entry(chain, e, rulenum - 1, saddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_LIST: ret = list_entries(chain, options&OPT_VERBOSE, options&OPT_NUMERIC, /*options&OPT_EXPANDED*/0, options&OPT_LINENUMBERS, handle); break; case CMD_FLUSH: ret = flush_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_ZERO: ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_LIST|CMD_ZERO: ret = list_entries(chain, options&OPT_VERBOSE, options&OPT_NUMERIC, /*options&OPT_EXPANDED*/0, options&OPT_LINENUMBERS, handle); if (ret) ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_NEW_CHAIN: ret = arptc_create_chain(chain, handle); break; case CMD_DELETE_CHAIN: ret = delete_chain(chain, options&OPT_VERBOSE, handle); break; case CMD_RENAME_CHAIN: ret = arptc_rename_chain(chain, newname, handle); break; case CMD_SET_POLICY: ret = arptc_set_policy(chain, policy, NULL, handle); break; default: /* We should never reach this... */ exit_tryhelp(2); } if (verbose > 1) dump_entries(*handle); return ret; } arptables-v0.0.3-4/include/0000700000175000017500000000000011345216210015640 5ustar bdschuymbdschuymarptables-v0.0.3-4/include/arptables_common.h0000600000175000017500000000132211345216210021336 0ustar bdschuymbdschuym#ifndef _ARPTABLES_COMMON_H #define _ARPTABLES_COMMON_H enum exittype { OTHER_PROBLEM = 1, PARAMETER_PROBLEM, VERSION_PROBLEM }; extern void exit_printhelp() __attribute__((noreturn)); extern void exit_tryhelp(int) __attribute__((noreturn)); int check_inverse(const char option[], int *invert, int *optind, int argc); extern int string_to_number(const char *, unsigned int, unsigned int, unsigned int *); extern int iptables_insmod(const char *modname, const char *modprobe); void exit_error(enum exittype, char *, ...)__attribute__((noreturn, format(printf,2,3))); extern const char *program_name, *program_version; extern void init_extensions(void); #endif /*_IPTABLES_COMMON_H*/ arptables-v0.0.3-4/include/arptables.h0000600000175000017500000001111311345216210017765 0ustar bdschuymbdschuym#ifndef _ARPTABLES_USER_H #define _ARPTABLES_USER_H #include "arptables_common.h" #include "libarptc/libarptc.h" /*******************************/ /* REMOVE LATER, PUT IN KERNEL */ /*******************************/ struct arpt_entry_match { int iets; }; /*******************************/ /* END OF KERNEL REPLACEMENTS */ /*******************************/ /* Include file for additions: new matches and targets. */ struct arptables_match { struct arptables_match *next; arpt_chainlabel name; const char *version; /* Size of match data. */ size_t size; /* Size of match data relevent for userspace comparison purposes */ size_t userspacesize; /* Function which prints out usage message. */ void (*help)(void); /* Initialize the match. */ void (*init)(struct arpt_entry_match *m, unsigned int *nfcache); /* Function which parses command options; returns true if it ate an option */ int (*parse)(int c, char **argv, int invert, unsigned int *flags, const struct arpt_entry *entry, unsigned int *nfcache, struct arpt_entry_match **match); /* Final check; exit if not ok. */ void (*final_check)(unsigned int flags); /* Prints out the match iff non-NULL: put space at end */ void (*print)(const struct arpt_arp *ip, const struct arpt_entry_match *match, int numeric); /* Saves the match info in parsable form to stdout. */ void (*save)(const struct arpt_arp *ip, const struct arpt_entry_match *match); /* Pointer to list of extra command-line options */ const struct option *extra_opts; /* Ignore these men behind the curtain: */ unsigned int option_offset; struct arpt_entry_match *m; unsigned int mflags; unsigned int used; unsigned int loaded; /* simulate loading so options are merged properly */ }; struct arptables_target { struct arptables_target *next; arpt_chainlabel name; const char *version; /* Size of target data. */ size_t size; /* Size of target data relevent for userspace comparison purposes */ size_t userspacesize; /* Function which prints out usage message. */ void (*help)(void); /* Initialize the target. */ void (*init)(struct arpt_entry_target *t); /* Function which parses command options; returns true if it ate an option */ int (*parse)(int c, char **argv, int invert, unsigned int *flags, const struct arpt_entry *entry, struct arpt_entry_target **target); /* Final check; exit if not ok. */ void (*final_check)(unsigned int flags); /* Prints out the target iff non-NULL: put space at end */ void (*print)(const struct arpt_arp *ip, const struct arpt_entry_target *target, int numeric); /* Saves the targinfo in parsable form to stdout. */ void (*save)(const struct arpt_arp *ip, const struct arpt_entry_target *target); /* Pointer to list of extra command-line options */ struct option *extra_opts; /* Ignore these men behind the curtain: */ unsigned int option_offset; struct arpt_entry_target *t; unsigned int tflags; unsigned int used; unsigned int loaded; /* simulate loading so options are merged properly */ }; /* Your shared library should call one of these. */ extern void register_match(struct arptables_match *me); extern void register_target(struct arptables_target *me); extern struct in_addr *dotted_to_addr(const char *dotted); extern char *addr_to_dotted(const struct in_addr *addrp); extern char *addr_to_anyname(const struct in_addr *addr); extern char *mask_to_dotted(const struct in_addr *mask); extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp, struct in_addr *maskp, unsigned int *naddrs); extern u_int16_t parse_protocol(const char *s); extern int do_command(int argc, char *argv[], char **table, arptc_handle_t *handle); /* Keeping track of external matches and targets: linked lists. */ extern struct arptables_match *arptables_matches; extern struct arptables_target *arptables_targets; enum arpt_tryload { DONT_LOAD, TRY_LOAD, LOAD_MUST_SUCCEED }; extern struct arptables_target *find_target(const char *name, enum arpt_tryload); extern struct arptables_match *find_match(const char *name, enum arpt_tryload); extern int delete_chain(const arpt_chainlabel chain, int verbose, arptc_handle_t *handle); extern int flush_entries(const arpt_chainlabel chain, int verbose, arptc_handle_t *handle); extern int for_each_chain(int (*fn)(const arpt_chainlabel, int, arptc_handle_t *), int verbose, int builtinstoo, arptc_handle_t *handle); struct in_addr *parse_hostnetwork(const char *name, unsigned int *naddrs); void print_mac(const unsigned char *mac, int l); #endif /*_ARPTABLES_USER_H*/ arptables-v0.0.3-4/include/arp_tables.h0000600000175000017500000002104011345216210020124 0ustar bdschuymbdschuym/* * Format of an ARP firewall descriptor * * src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in * network byte order. * flags are stored in host byte order (of course). */ #ifndef _ARPTABLES_H #define _ARPTABLES_H #ifdef __KERNEL__ #include #include #include #include #include #endif #include #define ARPT_FUNCTION_MAXNAMELEN 30 #define ARPT_TABLE_MAXNAMELEN 32 #define ARPT_DEV_ADDR_LEN_MAX 16 struct arpt_devaddr_info { char addr[ARPT_DEV_ADDR_LEN_MAX]; char mask[ARPT_DEV_ADDR_LEN_MAX]; }; /* Yes, Virginia, you have to zero the padding. */ struct arpt_arp { /* Source and target IP addr */ struct in_addr src, tgt; /* Mask for src and target IP addr */ struct in_addr smsk, tmsk; /* Device hw address length, src+target device addresses */ u_int8_t arhln, arhln_mask; struct arpt_devaddr_info src_devaddr; struct arpt_devaddr_info tgt_devaddr; /* ARP operation code. */ u_int16_t arpop, arpop_mask; /* ARP hardware address and protocol address format. */ u_int16_t arhrd, arhrd_mask; u_int16_t arpro, arpro_mask; /* The protocol address length is only accepted if it is 4 * so there is no use in offering a way to do filtering on it. */ char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; /* Flags word */ u_int8_t flags; /* Inverse flags */ u_int16_t invflags; }; struct arpt_entry_target { union { struct { u_int16_t target_size; /* Used by userspace */ char name[ARPT_FUNCTION_MAXNAMELEN]; } user; struct { u_int16_t target_size; /* Used inside the kernel */ struct arpt_target *target; } kernel; /* Total length */ u_int16_t target_size; } u; unsigned char data[0]; }; struct arpt_standard_target { struct arpt_entry_target target; int verdict; }; struct arpt_counters { u_int64_t pcnt, bcnt; /* Packet and byte counters */ }; /* Values for "flag" field in struct arpt_ip (general arp structure). * No flags defined yet. */ #define ARPT_F_MASK 0x00 /* All possible flag bits mask. */ /* Values for "inv" field in struct arpt_arp. */ #define ARPT_INV_VIA_IN 0x0001 /* Invert the sense of IN IFACE. */ #define ARPT_INV_VIA_OUT 0x0002 /* Invert the sense of OUT IFACE */ #define ARPT_INV_SRCIP 0x0004 /* Invert the sense of SRC IP. */ #define ARPT_INV_TGTIP 0x0008 /* Invert the sense of TGT IP. */ #define ARPT_INV_SRCDEVADDR 0x0010 /* Invert the sense of SRC DEV ADDR. */ #define ARPT_INV_TGTDEVADDR 0x0020 /* Invert the sense of TGT DEV ADDR. */ #define ARPT_INV_ARPOP 0x0040 /* Invert the sense of ARP OP. */ #define ARPT_INV_ARPHRD 0x0080 /* Invert the sense of ARP HRD. */ #define ARPT_INV_ARPPRO 0x0100 /* Invert the sense of ARP PRO. */ #define ARPT_INV_ARPHLN 0x0200 /* Invert the sense of ARP HLN. */ #define ARPT_INV_MASK 0x03FF /* All possible flag bits mask. */ /* This structure defines each of the firewall rules. Consists of 3 parts which are 1) general ARP header stuff 2) match specific stuff 3) the target to perform if the rule matches */ struct arpt_entry { struct arpt_arp arp; /* Size of arpt_entry + matches */ u_int16_t target_offset; /* Size of arpt_entry + matches + target */ u_int16_t next_offset; /* Back pointer */ unsigned int comefrom; /* Packet and byte counters. */ struct arpt_counters counters; /* The matches (if any), then the target. */ unsigned char elems[0]; }; /* * New IP firewall options for [gs]etsockopt at the RAW IP level. * Unlike BSD Linux inherits IP options so you don't have to use a raw * socket for this. Instead we check rights in the calls. */ #define ARPT_BASE_CTL 96 /* base for firewall socket options */ #define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL) #define ARPT_SO_SET_ADD_COUNTERS (ARPT_BASE_CTL + 1) #define ARPT_SO_SET_MAX ARPT_SO_SET_ADD_COUNTERS #define ARPT_SO_GET_INFO (ARPT_BASE_CTL) #define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1) #define ARPT_SO_GET_MAX ARPT_SO_GET_ENTRIES /* CONTINUE verdict for targets */ #define ARPT_CONTINUE 0xFFFFFFFF /* For standard target */ #define ARPT_RETURN (-NF_REPEAT - 1) /* The argument to ARPT_SO_GET_INFO */ struct arpt_getinfo { /* Which table: caller fills this in. */ char name[ARPT_TABLE_MAXNAMELEN]; /* Kernel fills these in. */ /* Which hook entry points are valid: bitmask */ unsigned int valid_hooks; /* Hook entry points: one per netfilter hook. */ unsigned int hook_entry[3]; /* Underflow points. */ unsigned int underflow[3]; /* Number of entries */ unsigned int num_entries; /* Size of entries. */ unsigned int size; }; /* The argument to ARPT_SO_SET_REPLACE. */ struct arpt_replace { /* Which table. */ char name[ARPT_TABLE_MAXNAMELEN]; /* Which hook entry points are valid: bitmask. You can't change this. */ unsigned int valid_hooks; /* Number of entries */ unsigned int num_entries; /* Total size of new entries */ unsigned int size; /* Hook entry points. */ unsigned int hook_entry[3]; /* Underflow points. */ unsigned int underflow[3]; /* Information about old entries: */ /* Number of counters (must be equal to current number of entries). */ unsigned int num_counters; /* The old entries' counters. */ struct arpt_counters *counters; /* The entries (hang off end: not really an array). */ struct arpt_entry entries[0]; }; /* The argument to ARPT_SO_ADD_COUNTERS. */ struct arpt_counters_info { /* Which table. */ char name[ARPT_TABLE_MAXNAMELEN]; unsigned int num_counters; /* The counters (actually `number' of these). */ struct arpt_counters counters[0]; }; /* The argument to ARPT_SO_GET_ENTRIES. */ struct arpt_get_entries { /* Which table: user fills this in. */ char name[ARPT_TABLE_MAXNAMELEN]; /* User fills this in: total entry size. */ unsigned int size; /* The entries. */ struct arpt_entry entrytable[0]; }; /* Standard return verdict, or do jump. */ #define ARPT_STANDARD_TARGET "" /* Error verdict. */ #define ARPT_ERROR_TARGET "ERROR" /* Helper functions */ static __inline__ struct arpt_entry_target *arpt_get_target(struct arpt_entry *e) { return (void *)e + e->target_offset; } /* fn returns 0 to continue iteration */ #define ARPT_ENTRY_ITERATE(entries, size, fn, args...) \ ({ \ unsigned int __i; \ int __ret = 0; \ struct arpt_entry *__entry; \ \ for (__i = 0; __i < (size); __i += __entry->next_offset) { \ __entry = (void *)(entries) + __i; \ \ __ret = fn(__entry , ## args); \ if (__ret != 0) \ break; \ } \ __ret; \ }) /* * Main firewall chains definitions and global var's definitions. */ #ifdef __KERNEL__ /* Registration hooks for targets. */ struct arpt_target { struct list_head list; const char name[ARPT_FUNCTION_MAXNAMELEN]; /* Returns verdict. */ unsigned int (*target)(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userdata); /* Called when user tries to insert an entry of this type: hook_mask is a bitmask of hooks from which it can be called. */ /* Should return true or false. */ int (*checkentry)(const char *tablename, const struct arpt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask); /* Called when entry of this type deleted. */ void (*destroy)(void *targinfo, unsigned int targinfosize); /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; }; extern int arpt_register_target(struct arpt_target *target); extern void arpt_unregister_target(struct arpt_target *target); /* Furniture shopping... */ struct arpt_table { struct list_head list; /* A unique name... */ char name[ARPT_TABLE_MAXNAMELEN]; /* Seed table: copied in register_table */ struct arpt_replace *table; /* What hooks you will enter on */ unsigned int valid_hooks; /* Lock for the curtain */ rwlock_t lock; /* Man behind the curtain... */ struct arpt_table_info *private; /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; }; extern int arpt_register_table(struct arpt_table *table); extern void arpt_unregister_table(struct arpt_table *table); extern unsigned int arpt_do_table(struct sk_buff **pskb, unsigned int hook, const struct net_device *in, const struct net_device *out, struct arpt_table *table, void *userdata); #define ARPT_ALIGN(s) (((s) + (__alignof__(struct arpt_entry)-1)) & ~(__alignof__(struct arpt_entry)-1)) #endif /*__KERNEL__*/ #endif /* _ARPTABLES_H */ arptables-v0.0.3-4/include/linux/0000700000175000017500000000000011345216210016777 5ustar bdschuymbdschuymarptables-v0.0.3-4/include/linux/netfilter_arp.h0000600000175000017500000000057411345216210022016 0ustar bdschuymbdschuym#ifndef __LINUX_ARP_NETFILTER_H #define __LINUX_ARP_NETFILTER_H /* ARP-specific defines for netfilter. * (C)2002 Rusty Russell IBM -- This code is GPL. */ #include /* There is no PF_ARP. */ #define NF_ARP 0 /* ARP Hooks */ #define NF_ARP_IN 0 #define NF_ARP_OUT 1 #define NF_ARP_FORWARD 2 #define NF_ARP_NUMHOOKS 3 #endif /* __LINUX_ARP_NETFILTER_H */ arptables-v0.0.3-4/include/linux/netfilter_arp/0000700000175000017500000000000011345216210021635 5ustar bdschuymbdschuymarptables-v0.0.3-4/include/linux/netfilter_arp/arp_tables.h0000600000175000017500000002104011345216210024121 0ustar bdschuymbdschuym/* * Format of an ARP firewall descriptor * * src, tgt, src_mask, tgt_mask, arpop, arpop_mask are always stored in * network byte order. * flags are stored in host byte order (of course). */ #ifndef _ARPTABLES_H #define _ARPTABLES_H #ifdef __KERNEL__ #include #include #include #include #include #endif #include #define ARPT_FUNCTION_MAXNAMELEN 30 #define ARPT_TABLE_MAXNAMELEN 32 #define ARPT_DEV_ADDR_LEN_MAX 16 struct arpt_devaddr_info { char addr[ARPT_DEV_ADDR_LEN_MAX]; char mask[ARPT_DEV_ADDR_LEN_MAX]; }; /* Yes, Virginia, you have to zero the padding. */ struct arpt_arp { /* Source and target IP addr */ struct in_addr src, tgt; /* Mask for src and target IP addr */ struct in_addr smsk, tmsk; /* Device hw address length, src+target device addresses */ u_int8_t arhln, arhln_mask; struct arpt_devaddr_info src_devaddr; struct arpt_devaddr_info tgt_devaddr; /* ARP operation code. */ u_int16_t arpop, arpop_mask; /* ARP hardware address and protocol address format. */ u_int16_t arhrd, arhrd_mask; u_int16_t arpro, arpro_mask; /* The protocol address length is only accepted if it is 4 * so there is no use in offering a way to do filtering on it. */ char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; /* Flags word */ u_int8_t flags; /* Inverse flags */ u_int16_t invflags; }; struct arpt_entry_target { union { struct { u_int16_t target_size; /* Used by userspace */ char name[ARPT_FUNCTION_MAXNAMELEN]; } user; struct { u_int16_t target_size; /* Used inside the kernel */ struct arpt_target *target; } kernel; /* Total length */ u_int16_t target_size; } u; unsigned char data[0]; }; struct arpt_standard_target { struct arpt_entry_target target; int verdict; }; struct arpt_counters { u_int64_t pcnt, bcnt; /* Packet and byte counters */ }; /* Values for "flag" field in struct arpt_ip (general arp structure). * No flags defined yet. */ #define ARPT_F_MASK 0x00 /* All possible flag bits mask. */ /* Values for "inv" field in struct arpt_arp. */ #define ARPT_INV_VIA_IN 0x0001 /* Invert the sense of IN IFACE. */ #define ARPT_INV_VIA_OUT 0x0002 /* Invert the sense of OUT IFACE */ #define ARPT_INV_SRCIP 0x0004 /* Invert the sense of SRC IP. */ #define ARPT_INV_TGTIP 0x0008 /* Invert the sense of TGT IP. */ #define ARPT_INV_SRCDEVADDR 0x0010 /* Invert the sense of SRC DEV ADDR. */ #define ARPT_INV_TGTDEVADDR 0x0020 /* Invert the sense of TGT DEV ADDR. */ #define ARPT_INV_ARPOP 0x0040 /* Invert the sense of ARP OP. */ #define ARPT_INV_ARPHRD 0x0080 /* Invert the sense of ARP HRD. */ #define ARPT_INV_ARPPRO 0x0100 /* Invert the sense of ARP PRO. */ #define ARPT_INV_ARPHLN 0x0200 /* Invert the sense of ARP HLN. */ #define ARPT_INV_MASK 0x03FF /* All possible flag bits mask. */ /* This structure defines each of the firewall rules. Consists of 3 parts which are 1) general ARP header stuff 2) match specific stuff 3) the target to perform if the rule matches */ struct arpt_entry { struct arpt_arp arp; /* Size of arpt_entry + matches */ u_int16_t target_offset; /* Size of arpt_entry + matches + target */ u_int16_t next_offset; /* Back pointer */ unsigned int comefrom; /* Packet and byte counters. */ struct arpt_counters counters; /* The matches (if any), then the target. */ unsigned char elems[0]; }; /* * New IP firewall options for [gs]etsockopt at the RAW IP level. * Unlike BSD Linux inherits IP options so you don't have to use a raw * socket for this. Instead we check rights in the calls. */ #define ARPT_BASE_CTL 96 /* base for firewall socket options */ #define ARPT_SO_SET_REPLACE (ARPT_BASE_CTL) #define ARPT_SO_SET_ADD_COUNTERS (ARPT_BASE_CTL + 1) #define ARPT_SO_SET_MAX ARPT_SO_SET_ADD_COUNTERS #define ARPT_SO_GET_INFO (ARPT_BASE_CTL) #define ARPT_SO_GET_ENTRIES (ARPT_BASE_CTL + 1) #define ARPT_SO_GET_MAX ARPT_SO_GET_ENTRIES /* CONTINUE verdict for targets */ #define ARPT_CONTINUE 0xFFFFFFFF /* For standard target */ #define ARPT_RETURN (-NF_REPEAT - 1) /* The argument to ARPT_SO_GET_INFO */ struct arpt_getinfo { /* Which table: caller fills this in. */ char name[ARPT_TABLE_MAXNAMELEN]; /* Kernel fills these in. */ /* Which hook entry points are valid: bitmask */ unsigned int valid_hooks; /* Hook entry points: one per netfilter hook. */ unsigned int hook_entry[3]; /* Underflow points. */ unsigned int underflow[3]; /* Number of entries */ unsigned int num_entries; /* Size of entries. */ unsigned int size; }; /* The argument to ARPT_SO_SET_REPLACE. */ struct arpt_replace { /* Which table. */ char name[ARPT_TABLE_MAXNAMELEN]; /* Which hook entry points are valid: bitmask. You can't change this. */ unsigned int valid_hooks; /* Number of entries */ unsigned int num_entries; /* Total size of new entries */ unsigned int size; /* Hook entry points. */ unsigned int hook_entry[3]; /* Underflow points. */ unsigned int underflow[3]; /* Information about old entries: */ /* Number of counters (must be equal to current number of entries). */ unsigned int num_counters; /* The old entries' counters. */ struct arpt_counters *counters; /* The entries (hang off end: not really an array). */ struct arpt_entry entries[0]; }; /* The argument to ARPT_SO_ADD_COUNTERS. */ struct arpt_counters_info { /* Which table. */ char name[ARPT_TABLE_MAXNAMELEN]; unsigned int num_counters; /* The counters (actually `number' of these). */ struct arpt_counters counters[0]; }; /* The argument to ARPT_SO_GET_ENTRIES. */ struct arpt_get_entries { /* Which table: user fills this in. */ char name[ARPT_TABLE_MAXNAMELEN]; /* User fills this in: total entry size. */ unsigned int size; /* The entries. */ struct arpt_entry entrytable[0]; }; /* Standard return verdict, or do jump. */ #define ARPT_STANDARD_TARGET "" /* Error verdict. */ #define ARPT_ERROR_TARGET "ERROR" /* Helper functions */ static __inline__ struct arpt_entry_target *arpt_get_target(struct arpt_entry *e) { return (void *)e + e->target_offset; } /* fn returns 0 to continue iteration */ #define ARPT_ENTRY_ITERATE(entries, size, fn, args...) \ ({ \ unsigned int __i; \ int __ret = 0; \ struct arpt_entry *__entry; \ \ for (__i = 0; __i < (size); __i += __entry->next_offset) { \ __entry = (void *)(entries) + __i; \ \ __ret = fn(__entry , ## args); \ if (__ret != 0) \ break; \ } \ __ret; \ }) /* * Main firewall chains definitions and global var's definitions. */ #ifdef __KERNEL__ /* Registration hooks for targets. */ struct arpt_target { struct list_head list; const char name[ARPT_FUNCTION_MAXNAMELEN]; /* Returns verdict. */ unsigned int (*target)(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, const void *targinfo, void *userdata); /* Called when user tries to insert an entry of this type: hook_mask is a bitmask of hooks from which it can be called. */ /* Should return true or false. */ int (*checkentry)(const char *tablename, const struct arpt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask); /* Called when entry of this type deleted. */ void (*destroy)(void *targinfo, unsigned int targinfosize); /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; }; extern int arpt_register_target(struct arpt_target *target); extern void arpt_unregister_target(struct arpt_target *target); /* Furniture shopping... */ struct arpt_table { struct list_head list; /* A unique name... */ char name[ARPT_TABLE_MAXNAMELEN]; /* Seed table: copied in register_table */ struct arpt_replace *table; /* What hooks you will enter on */ unsigned int valid_hooks; /* Lock for the curtain */ rwlock_t lock; /* Man behind the curtain... */ struct arpt_table_info *private; /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; }; extern int arpt_register_table(struct arpt_table *table); extern void arpt_unregister_table(struct arpt_table *table); extern unsigned int arpt_do_table(struct sk_buff **pskb, unsigned int hook, const struct net_device *in, const struct net_device *out, struct arpt_table *table, void *userdata); #define ARPT_ALIGN(s) (((s) + (__alignof__(struct arpt_entry)-1)) & ~(__alignof__(struct arpt_entry)-1)) #endif /*__KERNEL__*/ #endif /* _ARPTABLES_H */ arptables-v0.0.3-4/include/linux/netfilter_arp/arpt_mangle.h0000600000175000017500000000104311345216210024277 0ustar bdschuymbdschuym#ifndef _ARPT_MANGLE_H #define _ARPT_MANGLE_H #include #define ARPT_MANGLE_ADDR_LEN_MAX sizeof(struct in_addr) struct arpt_mangle { char src_devaddr[ARPT_DEV_ADDR_LEN_MAX]; char tgt_devaddr[ARPT_DEV_ADDR_LEN_MAX]; union { struct in_addr src_ip; } u_s; union { struct in_addr tgt_ip; } u_t; u_int8_t flags; int target; }; #define ARPT_MANGLE_SDEV 0x01 #define ARPT_MANGLE_TDEV 0x02 #define ARPT_MANGLE_SIP 0x04 #define ARPT_MANGLE_TIP 0x08 #define ARPT_MANGLE_MASK 0x0f #endif /* _ARPT_MANGLE_H */ arptables-v0.0.3-4/include/libarptc/0000700000175000017500000000000011345216210017440 5ustar bdschuymbdschuymarptables-v0.0.3-4/include/libarptc/arpt_kernel_headers.h0000600000175000017500000000121211345216210023610 0ustar bdschuymbdschuym/* This is the userspace/kernel interface for Generic IP Chains, required for libc6. */ #ifndef _FWCHAINS_KERNEL_HEADERS_H #define _FWCHAINS_KERNEL_HEADERS_H #include #if defined(__GLIBC__) && __GLIBC__ == 2 #include #include #include #include #include #include #include #include #else #include #include #include #include #include #include #include #include #include #endif #endif arptables-v0.0.3-4/include/libarptc/libarptc.h0000600000175000017500000001235511345216210021421 0ustar bdschuymbdschuym#ifndef _LIBARPTC_H #define _LIBARPTC_H /* Library which manipulates filtering rules. */ #include #include #ifndef ARPT_MIN_ALIGN /* arpt_entry has pointers and u_int64_t's in it, so if you align to it, you'll also align to any crazy matches and targets someone might write */ #define ARPT_MIN_ALIGN (__alignof__(struct arpt_entry)) #endif #define ARPT_ALIGN(s) (((s) + ((ARPT_MIN_ALIGN)-1)) & ~((ARPT_MIN_ALIGN)-1)) typedef char arpt_chainlabel[32]; #define ARPTC_LABEL_ACCEPT "ACCEPT" #define ARPTC_LABEL_DROP "DROP" #define ARPTC_LABEL_QUEUE "QUEUE" #define ARPTC_LABEL_RETURN "RETURN" /* NF_ARP_NUMHOOKS is different on 2.4 and 2.6; hack to support both */ extern int RUNTIME_NF_ARP_NUMHOOKS; /* boy, this is dirty */ /* Transparent handle type. */ typedef struct arptc_handle *arptc_handle_t; /* Does this chain exist? */ int arptc_is_chain(const char *chain, const arptc_handle_t handle); /* Take a snapshot of the rules. Returns NULL on error. */ arptc_handle_t arptc_init(const char *tablename); /* Iterator functions to run through the chains. Returns NULL at end. */ const char *arptc_first_chain(arptc_handle_t *handle); const char *arptc_next_chain(arptc_handle_t *handle); /* Get first rule in the given chain: NULL for empty chain. */ const struct arpt_entry *arptc_first_rule(const char *chain, arptc_handle_t *handle); /* Returns NULL when rules run out. */ const struct arpt_entry *arptc_next_rule(const struct arpt_entry *prev, arptc_handle_t *handle); /* Returns a pointer to the target name of this entry. */ const char *arptc_get_target(const struct arpt_entry *e, arptc_handle_t *handle); /* Is this a built-in chain? */ int arptc_builtin(const char *chain, const arptc_handle_t handle); /* Get the policy of a given built-in chain */ const char *arptc_get_policy(const char *chain, struct arpt_counters *counter, arptc_handle_t *handle); /* These functions return TRUE for OK or 0 and set errno. If errno == 0, it means there was a version error (ie. upgrade libarptc). */ /* Rule numbers start at 1 for the first rule. */ /* Insert the entry `e' in chain `chain' into position `rulenum'. */ int arptc_insert_entry(const arpt_chainlabel chain, const struct arpt_entry *e, unsigned int rulenum, arptc_handle_t *handle); /* Atomically replace rule `rulenum' in `chain' with `e'. */ int arptc_replace_entry(const arpt_chainlabel chain, const struct arpt_entry *e, unsigned int rulenum, arptc_handle_t *handle); /* Append entry `e' to chain `chain'. Equivalent to insert with rulenum = length of chain. */ int arptc_append_entry(const arpt_chainlabel chain, const struct arpt_entry *e, arptc_handle_t *handle); /* Delete the first rule in `chain' which matches `e', subject to matchmask (array of length == origfw) */ int arptc_delete_entry(const arpt_chainlabel chain, const struct arpt_entry *origfw, unsigned char *matchmask, arptc_handle_t *handle); /* Delete the rule in position `rulenum' in `chain'. */ int arptc_delete_num_entry(const arpt_chainlabel chain, unsigned int rulenum, arptc_handle_t *handle); /* Check the packet `e' on chain `chain'. Returns the verdict, or NULL and sets errno. */ const char *arptc_check_packet(const arpt_chainlabel chain, struct arpt_entry *entry, arptc_handle_t *handle); /* Flushes the entries in the given chain (ie. empties chain). */ int arptc_flush_entries(const arpt_chainlabel chain, arptc_handle_t *handle); /* Zeroes the counters in a chain. */ int arptc_zero_entries(const arpt_chainlabel chain, arptc_handle_t *handle); /* Creates a new chain. */ int arptc_create_chain(const arpt_chainlabel chain, arptc_handle_t *handle); /* Deletes a chain. */ int arptc_delete_chain(const arpt_chainlabel chain, arptc_handle_t *handle); /* Renames a chain. */ int arptc_rename_chain(const arpt_chainlabel oldname, const arpt_chainlabel newname, arptc_handle_t *handle); /* Sets the policy on a built-in chain. */ int arptc_set_policy(const arpt_chainlabel chain, const arpt_chainlabel policy, struct arpt_counters *counters, arptc_handle_t *handle); /* Get the number of references to this chain */ int arptc_get_references(unsigned int *ref, const arpt_chainlabel chain, arptc_handle_t *handle); /* read packet and byte counters for a specific rule */ struct arpt_counters *arptc_read_counter(const arpt_chainlabel chain, unsigned int rulenum, arptc_handle_t *handle); /* zero packet and byte counters for a specific rule */ int arptc_zero_counter(const arpt_chainlabel chain, unsigned int rulenum, arptc_handle_t *handle); /* set packet and byte counters for a specific rule */ int arptc_set_counter(const arpt_chainlabel chain, unsigned int rulenum, struct arpt_counters *counters, arptc_handle_t *handle); /* Makes the actual changes. */ int arptc_commit(arptc_handle_t *handle); /* Get raw socket. */ int arptc_get_raw_socket(); /* Translates errno numbers into more human-readable form than strerror. */ const char *arptc_strerror(int err); #endif /* _LIBARPTC_H */ arptables-v0.0.3-4/arptables-restore0000600000175000017500000000322311345216210017600 0ustar bdschuymbdschuym#!/usr/bin/perl -w # A script that imports text arptables rules. Similar to iptables-restore. use strict; my $tool = "__EXEC_PATH__/arptables"; my $table; my $rc; my $line; # ============================== # clear_arptables # - sets policy to accept # - flushes chains # - removes custom chains # ============================== sub clear_arptables { $rc = `$tool -P INPUT ACCEPT`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; $rc = `$tool -P FORWARD ACCEPT`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; $rc = `$tool -P OUTPUT ACCEPT`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; $rc = `$tool -F`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; $rc = `$tool -L`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; foreach $line (split("\n",$rc)) { unless ($line =~ m/Chain\s(.*?)\s\(.*references\)/) { next; } $rc = `$tool -X $1`; unless($? == 0) { print "ERROR: $rc\n"; exit -1 }; } } # ============================== unless (-x $tool) { print "ERROR: $tool isn't executable\n"; exit -1; }; &clear_arptables(); $line = 0; while(<>) { $line++; if(m/^#/) { next; }; if(m/^$/) { next; }; if(m/^\*(.*)/) { $table = $1; next; } # Process a chain directive if(m/^\:(.*?)\s(.*)/) { # is it a user or a built in chain ? if ("$2" eq "-") { $rc = `$tool -t $table -N $1`; unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1}; next; } $rc = `$tool -t $table -P $1 $2`; unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1}; next; } $rc = `$tool -t $table $_`; unless($? == 0) {print "ERROR(line $line): $rc\n"; exit -1}; } arptables-v0.0.3-4/arptables.80000600000175000017500000002471211345216210016273 0ustar bdschuymbdschuym.TH ARPTABLES 8 "August 2007" .\" .\" Man page originally written by Jochen Friedrich , .\" maintained by Bart De Schuymer. .\" It is based on the iptables man page. .\" .\" Iptables page by Herve Eychenne March 2000. .\" .\" 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. .\" .\" .SH NAME arptables (v.0.0.3-3) \- ARP table administration .SH SYNOPSIS .BR "arptables " [ "-t table" ] " -" [ AD ] " chain rule-specification " [ options ] .br .BR "arptables " [ "-t table" ] " -" [ RI ] " chain rulenum rule-specification " [ options ] .br .BR "arptables " [ "-t table" ] " -D chain rulenum " [ options ] .br .BR "arptables " [ "-t table" ] " -" [ "LFZ" ] " " [ chain ] " " [ options ] .br .BR "arptables " [ "-t table" ] " -" [ "NX" ] " chain" .br .BR "arptables " [ "-t table" ] " -E old-chain-name new-chain-name" .br .BR "arptables " [ "-t table" ] " -P chain target " [ options ] .SH DESCRIPTION .B arptables is a user space tool, it is used to set up and maintain the tables of ARP rules in the Linux kernel. These rules inspect the ARP frames which they see. .B arptables is analogous to the .B iptables user space tool, but .B arptables is less complicated. .SS CHAINS The kernel table is used to divide functionality into different sets of rules. Each set of rules is called a chain. Each chain is an ordered list of rules that can match ARP frames. If a rule matches an ARP frame, then a processing specification tells what to do with that matching frame. The processing specification is called a 'target'. However, if the frame does not match the current rule in the chain, then the next rule in the chain is examined and so forth. The user can create new (user-defined) chains which can be used as the 'target' of a rule. .SS TARGETS A firewall rule specifies criteria for an ARP frame and a frame processing specification called a target. When a frame matches a rule, then the next action performed by the kernel is specified by the target. The target can be one of these values: .IR ACCEPT , .IR DROP , .IR CONTINUE , .IR RETURN , an 'extension' (see below) or a user-defined chain. .PP .I ACCEPT means to let the frame through. .I DROP means the frame has to be dropped. .I CONTINUE means the next rule has to be checked. This can be handy to know how many frames pass a certain point in the chain or to log those frames. .I RETURN means stop traversing this chain and resume at the next rule in the previous (calling) chain. For the extension targets please see the .B "TARGET EXTENSIONS" section of this man page. .SS TABLES There is only one ARP table in the Linux kernel. The table is .BR filter. You can drop the '-t filter' argument to the arptables command. The -t argument must be the first argument on the arptables command line, if used. .TP .B "-t, --table" .br .BR filter , is the only table and contains two (Linux kernels 2.4.X) or three (Linux kernels 2.6.0 and later) built-in chains: .B INPUT (for frames destined for the host), .B OUTPUT (for locally-generated frames) and .B FORWARD (for frames being forwarded by the bridge code). The .B FORWARD chain doesn't exist in Linux 2.4.X kernels. .br .br .SH ARPTABLES COMMAND LINE ARGUMENTS After the initial arptables command line argument, the remaining arguments can be divided into several different groups. These groups are commands, miscellaneous commands, rule-specifications, match-extensions, and watcher-extensions. .SS COMMANDS The arptables command arguments specify the actions to perform on the table defined with the -t argument. If you do not use the -t argument to name a table, the commands apply to the default filter table. With the exception of the .B "-Z" command, only one command may be used on the command line at a time. .TP .B "-A, --append" Append a rule to the end of the selected chain. .TP .B "-D, --delete" Delete the specified rule from the selected chain. There are two ways to use this command. The first is by specifying an interval of rule numbers to delete, syntax: start_nr[:end_nr]. Using negative numbers is allowed, for more details about using negative numbers, see the -I command. The second usage is by specifying the complete rule as it would have been specified when it was added. .TP .B "-I, --insert" Insert the specified rule into the selected chain at the specified rule number. If the current number of rules equals N, then the specified number can be between -N and N+1. For a positive number i, it holds that i and i-N-1 specify the same place in the chain where the rule should be inserted. The number 0 specifies the place past the last rule in the chain and using this number is therefore equivalent with using the -A command. .TP .B "-R, --replace" Replaces the specified rule into the selected chain at the specified rule number. If the current number of rules equals N, then the specified number can be between 1 and N. i specifies the place in the chain where the rule should be replaced. .TP .B "-P, --policy" Set the policy for the chain to the given target. The policy can be .BR ACCEPT ", " DROP " or " RETURN . .TP .B "-F, --flush" Flush the selected chain. If no chain is selected, then every chain will be flushed. Flushing the chain does not change the policy of the chain, however. .TP .B "-Z, --zero" Set the counters of the selected chain to zero. If no chain is selected, all the counters are set to zero. The .B "-Z" command can be used in conjunction with the .B "-L" command. When both the .B "-Z" and .B "-L" commands are used together in this way, the rule counters are printed on the screen before they are set to zero. .TP .B "-L, --list" List all rules in the selected chain. If no chain is selected, all chains are listed. .TP .B "-N, --new-chain" Create a new user-defined chain with the given name. The number of user-defined chains is unlimited. A user-defined chain name has maximum length of 31 characters. .TP .B "-X, --delete-chain" Delete the specified user-defined chain. There must be no remaining references to the specified chain, otherwise .B arptables will refuse to delete it. If no chain is specified, all user-defined chains that aren't referenced will be removed. .TP .B "-E, --rename-chain" Rename the specified chain to a new name. Besides renaming a user-defined chain, you may rename a standard chain name to a name that suits your taste. For example, if you like PREBRIDGING more than PREROUTING, then you can use the -E command to rename the PREROUTING chain. If you do rename one of the standard .B arptables chain names, please be sure to mention this fact should you post a question on the .B arptables mailing lists. It would be wise to use the standard name in your post. Renaming a standard .B arptables chain in this fashion has no effect on the structure or function of the .B arptables kernel table. .SS MISCELLANOUS COMMANDS .TP .B "-V, --version" Show the version of the arptables userspace program. .TP .B "-h, --help" Give a brief description of the command syntax. .TP .BR "-j, --jump " "\fItarget\fP" The target of the rule. This is one of the following values: .BR ACCEPT , .BR DROP , .BR CONTINUE , .BR RETURN , a target extension (see .BR "TARGET EXTENSIONS" ")" or a user-defined chain name. .SS RULE-SPECIFICATIONS The following command line arguments make up a rule specification (as used in the add and delete commands). A "!" option before the specification inverts the test for that specification. Apart from these standard rule specifications there are some other command line arguments of interest. .TP .BR "-s, --source-ip " "[!] \fIaddress\fP[/\fImask]\fP" The Source IP specification. .TP .BR "-d, --destination-ip " "[!] \fIaddress\fP[/\fImask]\fP" The Destination IP specification. .TP .BR "--source-mac " "[!] \fIaddress\fP[/\fImask\fP]" The source mac address. Both mask and address are written as 6 hexadecimal numbers separated by colons. .TP .BR "--destination-mac " "[!] \fIaddress\fP[/\fImask\fP]" The destination mac address. Both mask and address are written as 6 hexadecimal numbers separated by colons. .TP .BR "-i, --in-interface " "[!] \fIname\fP" The interface via which a frame is received (for the .BR INPUT " and " FORWARD chains). The flag .B --in-if is an alias for this option. .TP .BR "-o, --out-interface " "[!] \fIname\fP" The interface via which a frame is going to be sent (for the .BR OUTPUT " and " FORWARD chains). The flag .B --out-if is an alias for this option. .TP .BR "-l, --h-length " "\fIlength\fP[/\fImask\fP]" The hardware length (nr of bytes) .TP .BR "--opcode " "\fIcode\fP[/\fImask\fP] The operation code (2 bytes). Available values are: .BR 1 = Request .BR 2 = Reply .BR 3 = Request_Reverse .BR 4 = Reply_Reverse .BR 5 = DRARP_Request .BR 6 = DRARP_Reply .BR 7 = DRARP_Error .BR 8 = InARP_Request .BR 9 = ARP_NAK . .TP .BR "--h-type " "\fItype\fP[/\fImask\fP]" The hardware type (2 bytes, hexadecimal). Available values are: .BR 1 = Ethernet . .TP .BR "--proto-type " "\fItype\fP[/\fImask\fP]" The protocol type (2 bytes). Available values are: .BR 0x800 = IPv4 . .SS TARGET-EXTENSIONS .B arptables extensions are precompiled into the userspace tool. So there is no need to explicitly load them with a -m option like in .BR iptables . However, these extensions deal with functionality supported by supplemental kernel modules. .SS mangle .TP .BR "--mangle-ip-s IP address" Mangles Source IP Address to given value. .TP .BR "--mangle-ip-d IP address" Mangles Destination IP Address to given value. .TP .BR "--mangle-mac-s MAC address" Mangles Source MAC Address to given value. .TP .BR "--mangle-mac-d MAC address" Mangles Destination MAC Address to given value. .TP .BR "--mangle-target target " Target of ARP mangle operation .BR "" ( DROP ", " CONTINUE " or " ACCEPT " -- default is " ACCEPT ). .SH MAILINGLISTS .I ebtables-user@lists.sourceforge.net .br .I ebtables-devel@lists.sourceforge.net .SH SEE ALSO .BR iptables "(8), " ebtables "(8), " arp "(8), " rarp "(8), " ifconfig "(8), " route (8) arptables-v0.0.3-4/arptables.sysv0000600000175000017500000000326511345216210017130 0ustar bdschuymbdschuym#!/bin/bash # # init script for arptables # # Original by Dag Wieers . # Modified/changed to arptables by # Rok Papez . # # chkconfig: - 16 84 # description: Arp filtering tables # # config: __SYSCONFIG__/arptables source /etc/init.d/functions source /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 [ -x __EXEC_PATH__/arptables ] || exit 1 [ -x __EXEC_PATH__/arptables-save ] || exit 1 [ -x __EXEC_PATH__/arptables-restore ] || exit 1 [ "$1" != "save" -o -r __SYSCONFIG__/arptables ] || exit 1 RETVAL=0 prog="arptables" desc="Arp filtering" start() { echo -n $"Starting $desc ($prog): " __EXEC_PATH__/arptables-restore < __SYSCONFIG__/arptables || RETVAL=1 if [ $RETVAL -eq 0 ]; then success "$prog startup" rm -f /var/lock/subsys/$prog else failure "$prog startup" fi echo return $RETVAL } stop() { echo -n $"Stopping $desc ($prog): " __EXEC_PATH__/arptables-restore < /dev/null || RETVAL=1 if [ $RETVAL -eq 0 ]; then success "$prog shutdown" rm -f %{_localstatedir}/lock/subsys/$prog else failure "$prog shutdown" fi echo return $RETVAL } restart() { stop start } save() { echo -n $"Saving $desc ($prog): " __EXEC_PATH__/arptables-save > __SYSCONFIG__/arptables || RETVAL=1 if [ $RETVAL -eq 0 ]; then success "$prog saved" else failure "$prog saved" fi echo } case "$1" in start) start ;; stop) stop ;; restart|reload) restart ;; condrestart) [ -e /var/lock/subsys/$prog ] && restart RETVAL=$? ;; save) save ;; status) __EXEC_PATH__/arptables-save RETVAL=$? ;; *) echo $"Usage $0 {start|stop|restart|condrestart|save|status}" RETVAL=1 esac exit $RETVAL arptables-v0.0.3-4/Makefile0000600000175000017500000000466011345216210015665 0ustar bdschuymbdschuymARPTABLES_VERSION:=0.0.3-4 KERNEL_DIR:=./ # default paths PREFIX:=/usr/local LIBDIR:=$(PREFIX)/lib BINDIR:=$(PREFIX)/sbin MANDIR:=$(PREFIX)/man INITDIR:=/etc/rc.d/init.d SYSCONFIGDIR:=/etc/sysconfig DESTDIR:= COPT_FLAGS:=-O2 CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include/ -Iinclude/ -DARPTABLES_VERSION=\"$(ARPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DARPTC_DEBUG ifndef ARPT_LIBDIR ARPT_LIBDIR:=$(LIBDIR)/arptables endif include extensions/Makefile all: arptables arptables.o: arptables.c $(CC) $(CFLAGS) -c -o $@ $< arptables-standalone.o: arptables-standalone.c $(CC) $(CFLAGS) -c -o $@ $< libarptc/libarptc.o: libarptc/libarptc.c libarptc/libarptc_incl.c $(CC) $(CFLAGS) -c -o $@ $< arptables: arptables-standalone.o arptables.o libarptc/libarptc.o $(EXT_OBJS) $(CC) $(CFLAGS) -o $@ $^ $(DESTDIR)$(MANDIR)/man8/arptables.8: arptables.8 mkdir -p $(@D) install -m 0644 -o root -g root $< $@ $(DESTDIR)$(BINDIR)/arptables: arptables mkdir -p $(DESTDIR)$(BINDIR) install -m 0755 -o root -g root $< $@ tmp1:=$(shell printf $(BINDIR) | sed 's/\//\\\//g') tmp2:=$(shell printf $(SYSCONFIGDIR) | sed 's/\//\\\//g') .PHONY: scripts scripts: arptables-save arptables-restore arptables.sysv cat arptables-save | sed 's/__EXEC_PATH__/$(tmp1)/g' > arptables-save_ install -m 0755 -o root -g root arptables-save_ $(DESTDIR)$(BINDIR)/arptables-save cat arptables-restore | sed 's/__EXEC_PATH__/$(tmp1)/g' > arptables-restore_ install -m 0755 -o root -g root arptables-restore_ $(DESTDIR)$(BINDIR)/arptables-restore cat arptables.sysv | sed 's/__EXEC_PATH__/$(tmp1)/g' | sed 's/__SYSCONFIG__/$(tmp2)/g' > arptables.sysv_ if test -d $(DESTDIR)$(INITDIR); then install -m 0755 -o root -g root arptables.sysv_ $(DESTDIR)$(INITDIR)/arptables; fi rm -f arptables-save_ arptables-restore_ arptables.sysv_ .PHONY: install install: $(DESTDIR)$(MANDIR)/man8/arptables.8 $(DESTDIR)$(BINDIR)/arptables scripts .PHONY: clean clean: rm -f arptables rm -f *.o *~ rm -f extensions/*.o extensions/*~ rm -f libarptc/*.o libarptc/*~ rm -f include/*~ include/libarptc/*~ DIR:=arptables-v$(ARPTABLES_VERSION) CVSDIRS:=CVS extensions/CVS libarptc/CVS include/CVS include/libarptc/CVS include/linux/CVS include/linux/netfilter_arp/CVS # This is used to make a new userspace release .PHONY: release release: rm -rf $(CVSDIRS) make clean cd ..;find $(DIR) -exec touch {} \;;find $(DIR) -exec chmod o-r,g-r,o-w,g-w,o-x,g-x {} \;;tar -pc $(DIR) | gzip >$(DIR).tar.gz arptables-v0.0.3-4/arptables-standalone.c0000600000175000017500000000342711345216210020474 0ustar bdschuymbdschuym/* * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au * * Based on the ipchains code by Paul Russell and Michael Neuling * * (C) 2000-2002 by the netfilter coreteam : * Paul 'Rusty' Russell * Marc Boucher * James Morris * Harald Welte * Jozsef Kadlecsik * * arptables -- IP firewall administration for kernels with * firewall table (aimed for the 2.3 kernels) * * See the accompanying manual page arptables(8) for information * about proper usage of this program. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include int main(int argc, char *argv[]) { int ret; char *table = "filter"; arptc_handle_t handle = NULL; program_name = "arptables"; /* init_extensions(); */ ret = do_command(argc, argv, &table, &handle); if (ret) ret = arptc_commit(&handle); if (!ret) fprintf(stderr, "arptables: %s\n", arptc_strerror(errno)); exit(!ret); } arptables-v0.0.3-4/libarptc/0000700000175000017500000000000011345216210016015 5ustar bdschuymbdschuymarptables-v0.0.3-4/libarptc/libarptc_incl.c0000600000175000017500000012446411345216210021003 0ustar bdschuymbdschuym/* Library which manipulates firewall rules. Version $Revision: 1.6 $ */ /* Architecture of firewall rules is as follows: * * Chains go INPUT, FORWARD, OUTPUT then user chains. * Each user chain starts with an ERROR node. * Every chain ends with an unconditional jump: a RETURN for user chains, * and a POLICY for built-ins. */ /* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See COPYING for details). */ #ifndef __OPTIMIZE__ STRUCT_ENTRY_TARGET * GET_TARGET(STRUCT_ENTRY *e) { return (void *)e + e->target_offset; } #endif static int sockfd = -1; static void *arptc_fn = NULL; static const char *hooknames[] = { [NF_ARP_IN] "INPUT", [NF_ARP_OUT] "OUTPUT", [NF_ARP_FORWARD] "FORWARD", }; struct counter_map { enum { COUNTER_MAP_NOMAP, COUNTER_MAP_NORMAL_MAP, COUNTER_MAP_ZEROED, COUNTER_MAP_SET } maptype; unsigned int mappos; }; /* Convenience structures */ struct arpt_error_target { STRUCT_ENTRY_TARGET t; char error[TABLE_MAXNAMELEN]; }; struct chain_cache { char name[TABLE_MAXNAMELEN]; /* This is the first rule in chain. */ STRUCT_ENTRY *start; /* Last rule in chain */ STRUCT_ENTRY *end; }; STRUCT_TC_HANDLE { /* Have changes been made? */ int changed; /* Size in here reflects original state. */ STRUCT_GETINFO info; struct counter_map *counter_map; /* Array of hook names */ const char **hooknames; /* Cached position of chain heads (NULL = no cache). */ unsigned int cache_num_chains; unsigned int cache_num_builtins; struct chain_cache *cache_chain_heads; /* Chain iterator: current chain cache entry. */ struct chain_cache *cache_chain_iteration; /* Rule iterator: terminal rule */ STRUCT_ENTRY *cache_rule_end; /* Number in here reflects current state. */ unsigned int new_number; STRUCT_GET_ENTRIES entries; }; static void set_changed(TC_HANDLE_T h) { if (h->cache_chain_heads) { free(h->cache_chain_heads); h->cache_chain_heads = NULL; h->cache_num_chains = 0; h->cache_chain_iteration = NULL; h->cache_rule_end = NULL; } h->changed = 1; } #ifdef ARPTC_DEBUG static void do_check(TC_HANDLE_T h, unsigned int line); #define CHECK(h) do { if (!getenv("ARPTC_NO_CHECK")) do_check((h), __LINE__); } while(0) #else #define CHECK(h) #endif static inline int get_number(const STRUCT_ENTRY *i, const STRUCT_ENTRY *seek, unsigned int *pos) { if (i == seek) return 1; (*pos)++; return 0; } static unsigned int entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek) { unsigned int pos = 0; if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, get_number, seek, &pos) == 0) { fprintf(stderr, "ERROR: offset %i not an entry!\n", (char *)seek - (char *)h->entries.entrytable); abort(); } return pos; } static inline int get_entry_n(STRUCT_ENTRY *i, unsigned int number, unsigned int *pos, STRUCT_ENTRY **pe) { if (*pos == number) { *pe = i; return 1; } (*pos)++; return 0; } static STRUCT_ENTRY * index2entry(TC_HANDLE_T h, unsigned int index) { unsigned int pos = 0; STRUCT_ENTRY *ret = NULL; ENTRY_ITERATE(h->entries.entrytable, h->entries.size, get_entry_n, index, &pos, &ret); return ret; } static inline STRUCT_ENTRY * get_entry(TC_HANDLE_T h, unsigned int offset) { return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset); } static inline unsigned long entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e) { return (char *)e - (char *)h->entries.entrytable; } static unsigned long index2offset(TC_HANDLE_T h, unsigned int index) { return entry2offset(h, index2entry(h, index)); } static const char * get_errorlabel(TC_HANDLE_T h, unsigned int offset) { STRUCT_ENTRY *e; e = get_entry(h, offset); if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) { fprintf(stderr, "ERROR: offset %u not an error node!\n", offset); abort(); } return (const char *)GET_TARGET(e)->data; } /* Allocate handle of given size */ static TC_HANDLE_T alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules) { size_t len; TC_HANDLE_T h; len = sizeof(STRUCT_TC_HANDLE) + size + num_rules * sizeof(struct counter_map); if ((h = malloc(len)) == NULL) { errno = ENOMEM; return NULL; } h->changed = 0; h->cache_num_chains = 0; h->cache_chain_heads = NULL; h->counter_map = (void *)h + sizeof(STRUCT_TC_HANDLE) + size; strcpy(h->info.name, tablename); strcpy(h->entries.name, tablename); return h; } TC_HANDLE_T TC_INIT(const char *tablename) { TC_HANDLE_T h; STRUCT_GETINFO info; unsigned int i; socklen_t s, tmp; arptc_fn = TC_INIT; if (sockfd != -1) close(sockfd); sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW); if (sockfd < 0) return NULL; s = sizeof(info); if (RUNTIME_NF_ARP_NUMHOOKS == 2) s -= 2 * sizeof(unsigned int); if (strlen(tablename) >= TABLE_MAXNAMELEN) { errno = EINVAL; return NULL; } strcpy(info.name, tablename); if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) return NULL; if (RUNTIME_NF_ARP_NUMHOOKS == 2) { memmove(&(info.hook_entry[3]), &(info.hook_entry[2]), 5 * sizeof(unsigned int)); memmove(&(info.underflow[3]), &(info.underflow[2]), 2 * sizeof(unsigned int)); } if ((h = alloc_handle(info.name, info.size, info.num_entries)) == NULL) return NULL; /* Too hard --RR */ #if 0 sprintf(pathname, "%s/%s", ARPT_LIB_DIR, info.name); dynlib = dlopen(pathname, RTLD_NOW); if (!dynlib) { errno = ENOENT; return NULL; } h->hooknames = dlsym(dynlib, "hooknames"); if (!h->hooknames) { errno = ENOENT; return NULL; } #else h->hooknames = hooknames; #endif /* Initialize current state */ h->info = info; h->new_number = h->info.num_entries; for (i = 0; i < h->info.num_entries; i++) h->counter_map[i] = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i}); h->entries.size = h->info.size; tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size; if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries, &tmp) < 0) { free(h); return NULL; } CHECK(h); return h; } /* static inline int print_match(const STRUCT_ENTRY_MATCH *m) { printf("Match name: `%s'\n", m->u.user.name); return 0; } */ static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle); void TC_DUMP_ENTRIES(const TC_HANDLE_T handle) { CHECK(handle); printf("libarptc v%s. %u entries, %u bytes.\n", ARPTABLES_VERSION, handle->new_number, handle->entries.size); printf("Table `%s'\n", handle->info.name); printf("Hooks: in/out = %u/%u\n", handle->info.hook_entry[NF_ARP_IN], handle->info.hook_entry[NF_ARP_OUT]); printf("Underflows: in/out = %u/%u\n", handle->info.underflow[NF_ARP_IN], handle->info.underflow[NF_ARP_OUT]); ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size, dump_entry, handle); } /* Returns 0 if not hook entry, else hooknumber + 1 */ static inline unsigned int is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h) { unsigned int i; for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { if ((h->info.valid_hooks & (1 << i)) && get_entry(h, h->info.hook_entry[i]) == e) return i+1; } return 0; } static inline int add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev) { unsigned int builtin; /* Last entry. End it. */ if (entry2offset(h, e) + e->next_offset == h->entries.size) { /* This is the ERROR node at end of the table */ h->cache_chain_heads[h->cache_num_chains-1].end = *prev; return 0; } /* We know this is the start of a new chain if it's an ERROR target, or a hook entry point */ if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) { /* prev was last entry in previous chain */ h->cache_chain_heads[h->cache_num_chains-1].end = *prev; strcpy(h->cache_chain_heads[h->cache_num_chains].name, (const char *)GET_TARGET(e)->data); h->cache_chain_heads[h->cache_num_chains].start = (void *)e + e->next_offset; h->cache_num_chains++; } else if ((builtin = is_hook_entry(e, h)) != 0) { if (h->cache_num_chains > 0) /* prev was last entry in previous chain */ h->cache_chain_heads[h->cache_num_chains-1].end = *prev; strcpy(h->cache_chain_heads[h->cache_num_chains].name, h->hooknames[builtin-1]); h->cache_chain_heads[h->cache_num_chains].start = (void *)e; h->cache_num_chains++; } *prev = e; return 0; } static int alphasort(const void *a, const void *b) { return strcmp(((struct chain_cache *)a)->name, ((struct chain_cache *)b)->name); } static int populate_cache(TC_HANDLE_T h) { unsigned int i; STRUCT_ENTRY *prev; /* # chains < # rules / 2 + num builtins - 1 */ h->cache_chain_heads = malloc((h->new_number / 2 + 4) * sizeof(struct chain_cache)); if (!h->cache_chain_heads) { errno = ENOMEM; return 0; } h->cache_num_chains = 0; h->cache_num_builtins = 0; /* Count builtins */ for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { if (h->info.valid_hooks & (1 << i)) h->cache_num_builtins++; } prev = NULL; ENTRY_ITERATE(h->entries.entrytable, h->entries.size, add_chain, h, &prev); qsort(h->cache_chain_heads + h->cache_num_builtins, h->cache_num_chains - h->cache_num_builtins, sizeof(struct chain_cache), alphasort); return 1; } /* Returns cache ptr if found, otherwise NULL. */ static struct chain_cache * find_label(const char *name, TC_HANDLE_T handle) { unsigned int i; if (handle->cache_chain_heads == NULL && !populate_cache(handle)) return NULL; /* FIXME: Linear search through builtins, then binary --RR */ for (i = 0; i < handle->cache_num_chains; i++) { if (strcmp(handle->cache_chain_heads[i].name, name) == 0) return &handle->cache_chain_heads[i]; } return NULL; } /* Does this chain exist? */ int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle) { return find_label(chain, handle) != NULL; } /* Returns the position of the final (ie. unconditional) element. */ static unsigned int get_chain_end(const TC_HANDLE_T handle, unsigned int start) { unsigned int last_off, off; STRUCT_ENTRY *e; last_off = start; e = get_entry(handle, start); /* Terminate when we meet a error label or a hook entry. */ for (off = start + e->next_offset; off < handle->entries.size; last_off = off, off += e->next_offset) { STRUCT_ENTRY_TARGET *t; unsigned int i; e = get_entry(handle, off); /* We hit an entry point. */ for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { if ((handle->info.valid_hooks & (1 << i)) && off == handle->info.hook_entry[i]) return last_off; } /* We hit a user chain label */ t = GET_TARGET(e); if (strcmp(t->u.user.name, ERROR_TARGET) == 0) return last_off; } /* SHOULD NEVER HAPPEN */ fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n", handle->entries.size, off); abort(); } /* Iterator functions to run through the chains. */ const char * TC_FIRST_CHAIN(TC_HANDLE_T *handle) { if ((*handle)->cache_chain_heads == NULL && !populate_cache(*handle)) return NULL; (*handle)->cache_chain_iteration = &(*handle)->cache_chain_heads[0]; return (*handle)->cache_chain_iteration->name; } /* Iterator functions to run through the chains. Returns NULL at end. */ const char * TC_NEXT_CHAIN(TC_HANDLE_T *handle) { (*handle)->cache_chain_iteration++; if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads == (*handle)->cache_num_chains) return NULL; return (*handle)->cache_chain_iteration->name; } /* Get first rule in the given chain: NULL for empty chain. */ const STRUCT_ENTRY * TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle) { struct chain_cache *c; c = find_label(chain, *handle); if (!c) { errno = ENOENT; return NULL; } /* Empty chain: single return/policy rule */ if (c->start == c->end) return NULL; (*handle)->cache_rule_end = c->end; return c->start; } /* Returns NULL when rules run out. */ const STRUCT_ENTRY * TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle) { if ((void *)prev + prev->next_offset == (void *)(*handle)->cache_rule_end) return NULL; return (void *)prev + prev->next_offset; } #if 0 /* How many rules in this chain? */ unsigned int TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle) { unsigned int off = 0; STRUCT_ENTRY *start, *end; CHECK(*handle); if (!find_label(&off, chain, *handle)) { errno = ENOENT; return (unsigned int)-1; } start = get_entry(*handle, off); end = get_entry(*handle, get_chain_end(*handle, off)); return entry2index(*handle, end) - entry2index(*handle, start); } /* Get n'th rule in this chain. */ const STRUCT_ENTRY *TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle) { unsigned int pos = 0, chainindex; CHECK(*handle); if (!find_label(&pos, chain, *handle)) { errno = ENOENT; return NULL; } chainindex = entry2index(*handle, get_entry(*handle, pos)); return index2entry(*handle, chainindex + n); } #endif static const char * target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce) { int spos; unsigned int labelidx; STRUCT_ENTRY *jumpto; /* To avoid const warnings */ STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce; if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) return GET_TARGET(e)->u.user.name; /* Standard target: evaluate */ spos = *(int *)GET_TARGET(e)->data; if (spos < 0) { if (spos == RETURN) return LABEL_RETURN; else if (spos == -NF_ACCEPT-1) return LABEL_ACCEPT; else if (spos == -NF_DROP-1) return LABEL_DROP; else if (spos == -NF_QUEUE-1) return LABEL_QUEUE; fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n", entry2offset(handle, e), handle->entries.size, spos); abort(); } jumpto = get_entry(handle, spos); /* Fall through rule */ if (jumpto == (void *)e + e->next_offset) return ""; /* Must point to head of a chain: ie. after error rule */ labelidx = entry2index(handle, jumpto) - 1; return get_errorlabel(handle, index2offset(handle, labelidx)); } /* Returns a pointer to the target name of this position. */ const char *TC_GET_TARGET(const STRUCT_ENTRY *e, TC_HANDLE_T *handle) { return target_name(*handle, e); } /* Is this a built-in chain? Actually returns hook + 1. */ int TC_BUILTIN(const char *chain, const TC_HANDLE_T handle) { unsigned int i; for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { if ((handle->info.valid_hooks & (1 << i)) && handle->hooknames[i] && strcmp(handle->hooknames[i], chain) == 0) return i+1; } return 0; } /* Get the policy of a given built-in chain */ const char * TC_GET_POLICY(const char *chain, STRUCT_COUNTERS *counters, TC_HANDLE_T *handle) { unsigned int start; STRUCT_ENTRY *e; int hook; hook = TC_BUILTIN(chain, *handle); if (hook != 0) start = (*handle)->info.hook_entry[hook-1]; else return NULL; e = get_entry(*handle, get_chain_end(*handle, start)); *counters = e->counters; return target_name(*handle, e); } static int correct_verdict(STRUCT_ENTRY *e, char *base, unsigned int offset, int delta_offset) { STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e); unsigned int curr = (char *)e - base; /* Trap: insert of fall-through rule. Don't change fall-through verdict to jump-over-next-rule. */ if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0 && t->verdict > (int)offset && !(curr == offset && t->verdict == curr + e->next_offset)) { t->verdict += delta_offset; } return 0; } /* Adjusts standard verdict jump positions after an insertion/deletion. */ static int set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle) { ENTRY_ITERATE((*handle)->entries.entrytable, (*handle)->entries.size, correct_verdict, (char *)(*handle)->entries.entrytable, offset, delta_offset); set_changed(*handle); return 1; } /* If prepend is set, then we are prepending to a chain: if the * insertion position is an entry point, keep the entry point. */ static int insert_rules(unsigned int num_rules, unsigned int rules_size, const STRUCT_ENTRY *insert, unsigned int offset, unsigned int num_rules_offset, int prepend, TC_HANDLE_T *handle) { TC_HANDLE_T newh; STRUCT_GETINFO newinfo; unsigned int i; if (offset >= (*handle)->entries.size) { errno = EINVAL; return 0; } newinfo = (*handle)->info; /* Fix up entry points. */ for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { /* Entry points to START of chain, so keep same if inserting on at that point. */ if ((*handle)->info.hook_entry[i] > offset) newinfo.hook_entry[i] += rules_size; /* Underflow always points to END of chain (policy), so if something is inserted at same point, it should be advanced. */ if ((*handle)->info.underflow[i] >= offset) newinfo.underflow[i] += rules_size; } newh = alloc_handle((*handle)->info.name, (*handle)->entries.size + rules_size, (*handle)->new_number + num_rules); if (!newh) return 0; newh->info = newinfo; /* Copy pre... */ memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset); /* ... Insert new ... */ memcpy((char *)newh->entries.entrytable + offset, insert, rules_size); /* ... copy post */ memcpy((char *)newh->entries.entrytable + offset + rules_size, (char *)(*handle)->entries.entrytable + offset, (*handle)->entries.size - offset); /* Move counter map. */ /* Copy pre... */ memcpy(newh->counter_map, (*handle)->counter_map, sizeof(struct counter_map) * num_rules_offset); /* ... copy post */ memcpy(newh->counter_map + num_rules_offset + num_rules, (*handle)->counter_map + num_rules_offset, sizeof(struct counter_map) * ((*handle)->new_number - num_rules_offset)); /* Set intermediates to no counter copy */ for (i = 0; i < num_rules; i++) newh->counter_map[num_rules_offset+i] = ((struct counter_map){ COUNTER_MAP_SET, 0 }); newh->new_number = (*handle)->new_number + num_rules; newh->entries.size = (*handle)->entries.size + rules_size; newh->hooknames = (*handle)->hooknames; if ((*handle)->cache_chain_heads) free((*handle)->cache_chain_heads); free(*handle); *handle = newh; return set_verdict(offset, rules_size, handle); } static int delete_rules(unsigned int num_rules, unsigned int rules_size, unsigned int offset, unsigned int num_rules_offset, TC_HANDLE_T *handle) { unsigned int i; if (offset + rules_size > (*handle)->entries.size) { errno = EINVAL; return 0; } /* Fix up entry points. */ for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { /* In practice, we never delete up to a hook entry, since the built-in chains are always first, so these two are never equal */ if ((*handle)->info.hook_entry[i] >= offset + rules_size) (*handle)->info.hook_entry[i] -= rules_size; else if ((*handle)->info.hook_entry[i] > offset) { fprintf(stderr, "ERROR: Deleting entry %u %u %u\n", i, (*handle)->info.hook_entry[i], offset); abort(); } /* Underflow points to policy (terminal) rule in built-in, so sequality is valid here (when deleting the last rule). */ if ((*handle)->info.underflow[i] >= offset + rules_size) (*handle)->info.underflow[i] -= rules_size; else if ((*handle)->info.underflow[i] > offset) { fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n", i, (*handle)->info.underflow[i], offset); abort(); } } /* Move the rules down. */ memmove((char *)(*handle)->entries.entrytable + offset, (char *)(*handle)->entries.entrytable + offset + rules_size, (*handle)->entries.size - (offset + rules_size)); /* Move the counter map down. */ memmove(&(*handle)->counter_map[num_rules_offset], &(*handle)->counter_map[num_rules_offset + num_rules], sizeof(struct counter_map) * ((*handle)->new_number - (num_rules + num_rules_offset))); /* Fix numbers */ (*handle)->new_number -= num_rules; (*handle)->entries.size -= rules_size; return set_verdict(offset, -(int)rules_size, handle); } static int standard_map(STRUCT_ENTRY *e, int verdict) { STRUCT_STANDARD_TARGET *t; t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); if (t->target.u.target_size != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) { errno = EINVAL; return 0; } /* memset for memcmp convenience on delete/replace */ memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN); strcpy(t->target.u.user.name, STANDARD_TARGET); t->verdict = verdict; return 1; } static int map_target(const TC_HANDLE_T handle, STRUCT_ENTRY *e, unsigned int offset, STRUCT_ENTRY_TARGET *old) { STRUCT_ENTRY_TARGET *t = GET_TARGET(e); /* Save old target (except data, which we don't change, except for standard case, where we don't care). */ *old = *t; /* Maybe it's empty (=> fall through) */ if (strcmp(t->u.user.name, "") == 0) return standard_map(e, offset + e->next_offset); /* Maybe it's a standard target name... */ else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0) return standard_map(e, -NF_ACCEPT - 1); else if (strcmp(t->u.user.name, LABEL_DROP) == 0) return standard_map(e, -NF_DROP - 1); else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0) return standard_map(e, -NF_QUEUE - 1); else if (strcmp(t->u.user.name, LABEL_RETURN) == 0) return standard_map(e, RETURN); else if (TC_BUILTIN(t->u.user.name, handle)) { /* Can't jump to builtins. */ errno = EINVAL; return 0; } else { /* Maybe it's an existing chain name. */ struct chain_cache *c; c = find_label(t->u.user.name, handle); if (c) return standard_map(e, entry2offset(handle, c->start)); } /* Must be a module? If not, kernel will reject... */ /* memset to all 0 for your memcmp convenience. */ memset(t->u.user.name + strlen(t->u.user.name), 0, FUNCTION_MAXNAMELEN - strlen(t->u.user.name)); return 1; } static void unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old) { STRUCT_ENTRY_TARGET *t = GET_TARGET(e); /* Save old target (except data, which we don't change, except for standard case, where we don't care). */ *t = *old; } /* Insert the entry `fw' in chain `chain' into position `rulenum'. */ int TC_INSERT_ENTRY(const ARPT_CHAINLABEL chain, const STRUCT_ENTRY *e, unsigned int rulenum, TC_HANDLE_T *handle) { unsigned int chainindex, offset; STRUCT_ENTRY_TARGET old; struct chain_cache *c; STRUCT_ENTRY *tmp; int ret; arptc_fn = TC_INSERT_ENTRY; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } chainindex = entry2index(*handle, c->start); tmp = index2entry(*handle, chainindex + rulenum); if (!tmp || tmp > c->end) { errno = E2BIG; return 0; } offset = index2offset(*handle, chainindex + rulenum); /* Mapping target actually alters entry, but that's transparent to the caller. */ if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old)) return 0; ret = insert_rules(1, e->next_offset, e, offset, chainindex + rulenum, rulenum == 0, handle); unmap_target((STRUCT_ENTRY *)e, &old); return ret; } /* Atomically replace rule `rulenum' in `chain' with `fw'. */ int TC_REPLACE_ENTRY(const ARPT_CHAINLABEL chain, const STRUCT_ENTRY *e, unsigned int rulenum, TC_HANDLE_T *handle) { unsigned int chainindex, offset; STRUCT_ENTRY_TARGET old; struct chain_cache *c; STRUCT_ENTRY *tmp; int ret; arptc_fn = TC_REPLACE_ENTRY; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } chainindex = entry2index(*handle, c->start); tmp = index2entry(*handle, chainindex + rulenum); if (!tmp || tmp >= c->end) { errno = E2BIG; return 0; } offset = index2offset(*handle, chainindex + rulenum); /* Replace = delete and insert. */ if (!delete_rules(1, get_entry(*handle, offset)->next_offset, offset, chainindex + rulenum, handle)) return 0; if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old)) return 0; ret = insert_rules(1, e->next_offset, e, offset, chainindex + rulenum, 1, handle); unmap_target((STRUCT_ENTRY *)e, &old); return ret; } /* Append entry `fw' to chain `chain'. Equivalent to insert with rulenum = length of chain. */ int TC_APPEND_ENTRY(const ARPT_CHAINLABEL chain, const STRUCT_ENTRY *e, TC_HANDLE_T *handle) { struct chain_cache *c; STRUCT_ENTRY_TARGET old; int ret; arptc_fn = TC_APPEND_ENTRY; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } if (!map_target(*handle, (STRUCT_ENTRY *)e, entry2offset(*handle, c->end), &old)) return 0; ret = insert_rules(1, e->next_offset, e, entry2offset(*handle, c->end), entry2index(*handle, c->end), 0, handle); unmap_target((STRUCT_ENTRY *)e, &old); return ret; } /* static inline int match_different(const STRUCT_ENTRY_MATCH *a, const unsigned char *a_elems, const unsigned char *b_elems, unsigned char **maskptr) { const STRUCT_ENTRY_MATCH *b; unsigned int i; */ /* Offset of b is the same as a. */ /* b = (void *)b_elems + ((unsigned char *)a - a_elems); if (a->u.match_size != b->u.match_size) return 1; if (strcmp(a->u.user.name, b->u.user.name) != 0) return 1; *maskptr += ALIGN(sizeof(*a)); for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++) if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0) return 1; *maskptr += i; return 0; } */ static inline int target_different(const unsigned char *a_targdata, const unsigned char *b_targdata, unsigned int tdatasize, const unsigned char *mask) { unsigned int i; for (i = 0; i < tdatasize; i++) if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0) return 1; return 0; } static int is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask); /* Delete the first rule in `chain' which matches `fw'. */ int TC_DELETE_ENTRY(const ARPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw, unsigned char *matchmask, TC_HANDLE_T *handle) { unsigned int offset; struct chain_cache *c; STRUCT_ENTRY *e, *fw; arptc_fn = TC_DELETE_ENTRY; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } fw = malloc(origfw->next_offset); if (fw == NULL) { errno = ENOMEM; return 0; } for (offset = entry2offset(*handle, c->start); offset < entry2offset(*handle, c->end); offset += e->next_offset) { STRUCT_ENTRY_TARGET discard; memcpy(fw, origfw, origfw->next_offset); /* FIXME: handle this in is_same --RR */ if (!map_target(*handle, fw, offset, &discard)) { free(fw); return 0; } e = get_entry(*handle, offset); #if 0 printf("Deleting:\n"); dump_entry(newe); #endif if (is_same(e, fw, matchmask)) { int ret; ret = delete_rules(1, e->next_offset, offset, entry2index(*handle, e), handle); free(fw); return ret; } } free(fw); errno = ENOENT; return 0; } /* Delete the rule in position `rulenum' in `chain'. */ int TC_DELETE_NUM_ENTRY(const ARPT_CHAINLABEL chain, unsigned int rulenum, TC_HANDLE_T *handle) { unsigned int index; int ret; STRUCT_ENTRY *e; struct chain_cache *c; arptc_fn = TC_DELETE_NUM_ENTRY; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } index = entry2index(*handle, c->start) + rulenum; if (index >= entry2index(*handle, c->end)) { errno = E2BIG; return 0; } e = index2entry(*handle, index); if (e == NULL) { errno = EINVAL; return 0; } ret = delete_rules(1, e->next_offset, entry2offset(*handle, e), index, handle); return ret; } /* Check the packet `fw' on chain `chain'. Returns the verdict, or NULL and sets errno. */ const char * TC_CHECK_PACKET(const ARPT_CHAINLABEL chain, STRUCT_ENTRY *entry, TC_HANDLE_T *handle) { errno = ENOSYS; return NULL; } /* Flushes the entries in the given chain (ie. empties chain). */ int TC_FLUSH_ENTRIES(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle) { unsigned int startindex, endindex; struct chain_cache *c; int ret; arptc_fn = TC_FLUSH_ENTRIES; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } startindex = entry2index(*handle, c->start); endindex = entry2index(*handle, c->end); ret = delete_rules(endindex - startindex, (char *)c->end - (char *)c->start, entry2offset(*handle, c->start), startindex, handle); return ret; } /* Zeroes the counters in a chain. */ int TC_ZERO_ENTRIES(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle) { unsigned int i, end; struct chain_cache *c; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } i = entry2index(*handle, c->start); end = entry2index(*handle, c->end); for (; i <= end; i++) { if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP) (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED; } set_changed(*handle); return 1; } STRUCT_COUNTERS * TC_READ_COUNTER(const ARPT_CHAINLABEL chain, unsigned int rulenum, TC_HANDLE_T *handle) { STRUCT_ENTRY *e; struct chain_cache *c; unsigned int chainindex, end; arptc_fn = TC_READ_COUNTER; CHECK(*handle); if (!(c = find_label(chain, *handle))) { errno = ENOENT; return NULL; } chainindex = entry2index(*handle, c->start); end = entry2index(*handle, c->end); if (chainindex + rulenum > end) { errno = E2BIG; return NULL; } e = index2entry(*handle, chainindex + rulenum); return &e->counters; } int TC_ZERO_COUNTER(const ARPT_CHAINLABEL chain, unsigned int rulenum, TC_HANDLE_T *handle) { STRUCT_ENTRY *e; struct chain_cache *c; unsigned int chainindex, end; arptc_fn = TC_ZERO_COUNTER; CHECK(*handle); if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } chainindex = entry2index(*handle, c->start); end = entry2index(*handle, c->end); if (chainindex + rulenum > end) { errno = E2BIG; return 0; } e = index2entry(*handle, chainindex + rulenum); if ((*handle)->counter_map[chainindex + rulenum].maptype == COUNTER_MAP_NORMAL_MAP) { (*handle)->counter_map[chainindex + rulenum].maptype = COUNTER_MAP_ZEROED; } set_changed(*handle); return 1; } int TC_SET_COUNTER(const ARPT_CHAINLABEL chain, unsigned int rulenum, STRUCT_COUNTERS *counters, TC_HANDLE_T *handle) { STRUCT_ENTRY *e; struct chain_cache *c; unsigned int chainindex, end; arptc_fn = TC_SET_COUNTER; CHECK(*handle); if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } chainindex = entry2index(*handle, c->start); end = entry2index(*handle, c->end); if (chainindex + rulenum > end) { errno = E2BIG; return 0; } e = index2entry(*handle, chainindex + rulenum); (*handle)->counter_map[chainindex + rulenum].maptype = COUNTER_MAP_SET; memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS)); set_changed(*handle); return 1; } /* Creates a new chain. */ /* To create a chain, create two rules: error node and unconditional * return. */ int TC_CREATE_CHAIN(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle) { int ret; struct { STRUCT_ENTRY head; struct arpt_error_target name; STRUCT_ENTRY ret; STRUCT_STANDARD_TARGET target; } newc; arptc_fn = TC_CREATE_CHAIN; /* find_label doesn't cover built-in targets: DROP, ACCEPT, QUEUE, RETURN. */ if (find_label(chain, *handle) || strcmp(chain, LABEL_DROP) == 0 || strcmp(chain, LABEL_ACCEPT) == 0 || strcmp(chain, LABEL_QUEUE) == 0 || strcmp(chain, LABEL_RETURN) == 0) { errno = EEXIST; return 0; } if (strlen(chain)+1 > sizeof(ARPT_CHAINLABEL)) { errno = EINVAL; return 0; } memset(&newc, 0, sizeof(newc)); newc.head.target_offset = sizeof(STRUCT_ENTRY); newc.head.next_offset = sizeof(STRUCT_ENTRY) + ALIGN(sizeof(struct arpt_error_target)); strcpy(newc.name.t.u.user.name, ERROR_TARGET); newc.name.t.u.target_size = ALIGN(sizeof(struct arpt_error_target)); strcpy(newc.name.error, chain); newc.ret.target_offset = sizeof(STRUCT_ENTRY); newc.ret.next_offset = sizeof(STRUCT_ENTRY) + ALIGN(sizeof(STRUCT_STANDARD_TARGET)); strcpy(newc.target.target.u.user.name, STANDARD_TARGET); newc.target.target.u.target_size = ALIGN(sizeof(STRUCT_STANDARD_TARGET)); newc.target.verdict = RETURN; /* Add just before terminal entry */ ret = insert_rules(2, sizeof(newc), &newc.head, index2offset(*handle, (*handle)->new_number - 1), (*handle)->new_number - 1, 0, handle); return ret; } static int count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref) { STRUCT_STANDARD_TARGET *t; if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) { t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); if (t->verdict == offset) (*ref)++; } return 0; } /* Get the number of references to this chain. */ int TC_GET_REFERENCES(unsigned int *ref, const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle) { struct chain_cache *c; if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } *ref = 0; ENTRY_ITERATE((*handle)->entries.entrytable, (*handle)->entries.size, count_ref, entry2offset(*handle, c->start), ref); return 1; } /* Deletes a chain. */ int TC_DELETE_CHAIN(const ARPT_CHAINLABEL chain, TC_HANDLE_T *handle) { unsigned int labelidx, labeloff; unsigned int references; struct chain_cache *c; int ret; if (!TC_GET_REFERENCES(&references, chain, handle)) return 0; arptc_fn = TC_DELETE_CHAIN; if (TC_BUILTIN(chain, *handle)) { errno = EINVAL; return 0; } if (references > 0) { errno = EMLINK; return 0; } if (!(c = find_label(chain, *handle))) { errno = ENOENT; return 0; } if ((void *)c->start != c->end) { errno = ENOTEMPTY; return 0; } /* Need label index: preceeds chain start */ labelidx = entry2index(*handle, c->start) - 1; labeloff = index2offset(*handle, labelidx); ret = delete_rules(2, get_entry(*handle, labeloff)->next_offset + c->start->next_offset, labeloff, labelidx, handle); return ret; } /* Renames a chain. */ int TC_RENAME_CHAIN(const ARPT_CHAINLABEL oldname, const ARPT_CHAINLABEL newname, TC_HANDLE_T *handle) { unsigned int labeloff, labelidx; struct chain_cache *c; struct arpt_error_target *t; arptc_fn = TC_RENAME_CHAIN; /* find_label doesn't cover built-in targets: DROP, ACCEPT, QUEUE, RETURN. */ if (find_label(newname, *handle) || strcmp(newname, LABEL_DROP) == 0 || strcmp(newname, LABEL_ACCEPT) == 0 || strcmp(newname, LABEL_QUEUE) == 0 || strcmp(newname, LABEL_RETURN) == 0) { errno = EEXIST; return 0; } if (!(c = find_label(oldname, *handle)) || TC_BUILTIN(oldname, *handle)) { errno = ENOENT; return 0; } if (strlen(newname)+1 > sizeof(ARPT_CHAINLABEL)) { errno = EINVAL; return 0; } /* Need label index: preceeds chain start */ labelidx = entry2index(*handle, c->start) - 1; labeloff = index2offset(*handle, labelidx); t = (struct arpt_error_target *) GET_TARGET(get_entry(*handle, labeloff)); memset(t->error, 0, sizeof(t->error)); strcpy(t->error, newname); set_changed(*handle); return 1; } /* Sets the policy on a built-in chain. */ int TC_SET_POLICY(const ARPT_CHAINLABEL chain, const ARPT_CHAINLABEL policy, STRUCT_COUNTERS *counters, TC_HANDLE_T *handle) { unsigned int hook; unsigned int policyoff, ctrindex; STRUCT_ENTRY *e; STRUCT_STANDARD_TARGET *t; arptc_fn = TC_SET_POLICY; /* Figure out which chain. */ hook = TC_BUILTIN(chain, *handle); if (hook == 0) { errno = ENOENT; return 0; } else hook--; policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]); if (policyoff != (*handle)->info.underflow[hook]) { printf("ERROR: Policy for `%s' offset %u != underflow %u\n", chain, policyoff, (*handle)->info.underflow[hook]); return 0; } e = get_entry(*handle, policyoff); t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); if (strcmp(policy, LABEL_ACCEPT) == 0) t->verdict = -NF_ACCEPT - 1; else if (strcmp(policy, LABEL_DROP) == 0) t->verdict = -NF_DROP - 1; else { errno = EINVAL; return 0; } ctrindex = entry2index(*handle, e); if (counters) { /* set byte and packet counters */ memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS)); (*handle)->counter_map[ctrindex].maptype = COUNTER_MAP_SET; } else { (*handle)->counter_map[ctrindex] = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 }); } set_changed(*handle); return 1; } /* Without this, on gcc 2.7.2.3, we get: libarptc.c: In function `TC_COMMIT': libarptc.c:833: fixed or forbidden register was spilled. This may be due to a compiler bug or to impossible asm statements or clauses. */ static void subtract_counters(STRUCT_COUNTERS *answer, const STRUCT_COUNTERS *a, const STRUCT_COUNTERS *b) { answer->pcnt = a->pcnt - b->pcnt; answer->bcnt = a->bcnt - b->bcnt; } int TC_COMMIT(TC_HANDLE_T *handle) { /* Replace, then map back the counters. */ STRUCT_REPLACE *repl; STRUCT_COUNTERS_INFO *newcounters; unsigned int i; size_t counterlen = sizeof(STRUCT_COUNTERS_INFO) + sizeof(STRUCT_COUNTERS) * (*handle)->new_number; int sizeof_repl = sizeof(*repl); CHECK(*handle); #if 0 TC_DUMP_ENTRIES(*handle); #endif /* Don't commit if nothing changed. */ if (!(*handle)->changed) goto finished; /* allocate a bit more than needed for ease */ repl = malloc(2 * sizeof(*repl) + (*handle)->entries.size); if (!repl) { errno = ENOMEM; return 0; } /* These are the old counters we will get from kernel */ repl->counters = malloc(sizeof(STRUCT_COUNTERS) * (*handle)->info.num_entries); if (!repl->counters) { free(repl); errno = ENOMEM; return 0; } /* These are the counters we're going to put back, later. */ newcounters = malloc(counterlen); if (!newcounters) { free(repl->counters); free(repl); errno = ENOMEM; return 0; } strcpy(repl->name, (*handle)->info.name); repl->num_entries = (*handle)->new_number; repl->size = (*handle)->entries.size; memcpy(repl->hook_entry, (*handle)->info.hook_entry, sizeof(repl->hook_entry)); memcpy(repl->underflow, (*handle)->info.underflow, sizeof(repl->underflow)); repl->num_counters = (*handle)->info.num_entries; repl->valid_hooks = (*handle)->info.valid_hooks; memcpy(repl->entries, (*handle)->entries.entrytable, (*handle)->entries.size); if (RUNTIME_NF_ARP_NUMHOOKS == 2) { memmove(&(repl->underflow[2]), &(repl->underflow[3]), ((*handle)->entries.size) + sizeof(struct arpt_replace)); memmove(&(repl->hook_entry[2]), &(repl->hook_entry[3]), ((*handle)->entries.size) + sizeof(struct arpt_replace)); sizeof_repl -= 2 * sizeof(unsigned int); } if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl, sizeof_repl + (*handle)->entries.size) < 0) { free(repl->counters); free(repl); free(newcounters); return 0; } if (RUNTIME_NF_ARP_NUMHOOKS == 2) { memmove(&(repl->hook_entry[3]), &(repl->hook_entry[2]), ((*handle)->entries.size) + sizeof(struct arpt_replace)); memmove(&(repl->underflow[3]), &(repl->underflow[2]), ((*handle)->entries.size) + sizeof(struct arpt_replace)); } /* Put counters back. */ strcpy(newcounters->name, (*handle)->info.name); newcounters->num_counters = (*handle)->new_number; for (i = 0; i < (*handle)->new_number; i++) { unsigned int mappos = (*handle)->counter_map[i].mappos; switch ((*handle)->counter_map[i].maptype) { case COUNTER_MAP_NOMAP: newcounters->counters[i] = ((STRUCT_COUNTERS){ 0, 0 }); break; case COUNTER_MAP_NORMAL_MAP: /* Original read: X. * Atomic read on replacement: X + Y. * Currently in kernel: Z. * Want in kernel: X + Y + Z. * => Add in X + Y * => Add in replacement read. */ newcounters->counters[i] = repl->counters[mappos]; break; case COUNTER_MAP_ZEROED: /* Original read: X. * Atomic read on replacement: X + Y. * Currently in kernel: Z. * Want in kernel: Y + Z. * => Add in Y. * => Add in (replacement read - original read). */ subtract_counters(&newcounters->counters[i], &repl->counters[mappos], &index2entry(*handle, i)->counters); break; case COUNTER_MAP_SET: /* Want to set counter (iptables-restore) */ memcpy(&newcounters->counters[i], &index2entry(*handle, i)->counters, sizeof(STRUCT_COUNTERS)); break; } } #ifdef KERNEL_64_USERSPACE_32 { /* Kernel will think that pointer should be 64-bits, and get padding. So we accomodate here (assumption: alignment of `counters' is on 64-bit boundary). */ u_int64_t *kernptr = (u_int64_t *)&newcounters->counters; if ((unsigned long)&newcounters->counters % 8 != 0) { fprintf(stderr, "counters alignment incorrect! Mail rusty!\n"); abort(); } *kernptr = newcounters->counters; } #endif /* KERNEL_64_USERSPACE_32 */ if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS, newcounters, counterlen) < 0) { free(repl->counters); free(repl); free(newcounters); return 0; } free(repl->counters); free(repl); free(newcounters); finished: if ((*handle)->cache_chain_heads) free((*handle)->cache_chain_heads); free(*handle); *handle = NULL; return 1; } /* Get raw socket. */ int TC_GET_RAW_SOCKET() { return sockfd; } /* Translates errno numbers into more human-readable form than strerror. */ const char * TC_STRERROR(int err) { unsigned int i; struct table_struct { void *fn; int err; const char *message; } table [] = { { TC_INIT, EPERM, "Permission denied (you must be root)" }, { TC_INIT, EINVAL, "Module is wrong version" }, { TC_INIT, ENOENT, "Table does not exist (do you need to insmod?)" }, { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" }, { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" }, { TC_DELETE_CHAIN, EMLINK, "Can't delete chain with references left" }, { TC_CREATE_CHAIN, EEXIST, "Chain already exists" }, { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" }, { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" }, { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" }, { TC_READ_COUNTER, E2BIG, "Index of counter too big" }, { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, { TC_INSERT_ENTRY, ELOOP, "Loop found in table" }, { TC_INSERT_ENTRY, EINVAL, "Target problem" }, /* EINVAL for CHECK probably means bad interface. */ { TC_CHECK_PACKET, EINVAL, "Bad arguments (does that interface exist?)" }, { TC_CHECK_PACKET, ENOSYS, "Checking will most likely never get implemented" }, /* ENOENT for DELETE probably means no matching rule */ { TC_DELETE_ENTRY, ENOENT, "Bad rule (does a matching rule exist in that chain?)" }, { TC_SET_POLICY, ENOENT, "Bad built-in chain name" }, { TC_SET_POLICY, EINVAL, "Bad policy name" }, { NULL, 0, "Incompatible with this kernel" }, { NULL, ENOPROTOOPT, "arptables who? (do you need to insmod?)" }, { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" }, { NULL, ENOMEM, "Memory allocation problem" }, { NULL, ENOENT, "No chain/target/match by that name" }, }; for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) { if ((!table[i].fn || table[i].fn == arptc_fn) && table[i].err == err) return table[i].message; } return strerror(err); } arptables-v0.0.3-4/libarptc/libarptc.c0000600000175000017500000003662111345216210017773 0ustar bdschuymbdschuym/* Library which manipulates firewall rules. Version 0.1. */ /* Architecture of firewall rules is as follows: * * Chains go INPUT, FORWARD, OUTPUT then user chains. * Each user chain starts with an ERROR node. * Every chain ends with an unconditional jump: a RETURN for user chains, * and a POLICY for built-ins. */ /* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See COPYING for details). */ #include #include #include #include #include #include #ifdef DEBUG_CONNTRACK #define inline #endif #if !defined(__GLIBC__) || (__GLIBC__ < 2) typedef unsigned int socklen_t; #endif #include "libarptc/libarptc.h" #define IP_VERSION 4 #define IP_OFFSET 0x1FFF #define STRUCT_ENTRY_TARGET struct arpt_entry_target #define STRUCT_ENTRY struct arpt_entry #define STRUCT_ENTRY_MATCH struct arpt_entry_match #define STRUCT_GETINFO struct arpt_getinfo #define STRUCT_GET_ENTRIES struct arpt_get_entries #define STRUCT_COUNTERS struct arpt_counters #define STRUCT_COUNTERS_INFO struct arpt_counters_info #define STRUCT_STANDARD_TARGET struct arpt_standard_target #define STRUCT_REPLACE struct arpt_replace #define STRUCT_TC_HANDLE struct arptc_handle #define TC_HANDLE_T arptc_handle_t #define ENTRY_ITERATE ARPT_ENTRY_ITERATE #define TABLE_MAXNAMELEN ARPT_TABLE_MAXNAMELEN #define FUNCTION_MAXNAMELEN ARPT_FUNCTION_MAXNAMELEN #define GET_TARGET arpt_get_target #define ERROR_TARGET ARPT_ERROR_TARGET #define ARPT_CHAINLABEL arpt_chainlabel #define TC_DUMP_ENTRIES dump_entries #define TC_IS_CHAIN arptc_is_chain #define TC_FIRST_CHAIN arptc_first_chain #define TC_NEXT_CHAIN arptc_next_chain #define TC_FIRST_RULE arptc_first_rule #define TC_NEXT_RULE arptc_next_rule #define TC_GET_TARGET arptc_get_target #define TC_BUILTIN arptc_builtin #define TC_GET_POLICY arptc_get_policy #define TC_INSERT_ENTRY arptc_insert_entry #define TC_REPLACE_ENTRY arptc_replace_entry #define TC_APPEND_ENTRY arptc_append_entry #define TC_DELETE_ENTRY arptc_delete_entry #define TC_DELETE_NUM_ENTRY arptc_delete_num_entry #define TC_CHECK_PACKET arptc_check_packet #define TC_FLUSH_ENTRIES arptc_flush_entries #define TC_ZERO_ENTRIES arptc_zero_entries #define TC_READ_COUNTER arptc_read_counter #define TC_ZERO_COUNTER arptc_zero_counter #define TC_SET_COUNTER arptc_set_counter #define TC_CREATE_CHAIN arptc_create_chain #define TC_GET_REFERENCES arptc_get_references #define TC_DELETE_CHAIN arptc_delete_chain #define TC_RENAME_CHAIN arptc_rename_chain #define TC_SET_POLICY arptc_set_policy #define TC_GET_RAW_SOCKET arptc_get_raw_socket #define TC_INIT arptc_init #define TC_COMMIT arptc_commit #define TC_STRERROR arptc_strerror #define TC_AF AF_INET #define TC_IPPROTO IPPROTO_IP #define SO_SET_REPLACE ARPT_SO_SET_REPLACE #define SO_SET_ADD_COUNTERS ARPT_SO_SET_ADD_COUNTERS #define SO_GET_INFO ARPT_SO_GET_INFO #define SO_GET_ENTRIES ARPT_SO_GET_ENTRIES #define SO_GET_VERSION ARPT_SO_GET_VERSION #define STANDARD_TARGET ARPT_STANDARD_TARGET #define LABEL_RETURN ARPTC_LABEL_RETURN #define LABEL_ACCEPT ARPTC_LABEL_ACCEPT #define LABEL_DROP ARPTC_LABEL_DROP #define LABEL_QUEUE ARPTC_LABEL_QUEUE #define ALIGN ARPT_ALIGN #define RETURN ARPT_RETURN #include "libarptc_incl.c" #define IP_PARTS_NATIVE(n) \ (unsigned int)((n)>>24)&0xFF, \ (unsigned int)((n)>>16)&0xFF, \ (unsigned int)((n)>>8)&0xFF, \ (unsigned int)((n)&0xFF) #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n)) int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle) { size_t i; STRUCT_ENTRY_TARGET *t; printf("Entry %u (%lu):\n", entry2index(handle, e), entry2offset(handle, e)); printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n", IP_PARTS(e->arp.src.s_addr),IP_PARTS(e->arp.smsk.s_addr)); printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n", IP_PARTS(e->arp.tgt.s_addr),IP_PARTS(e->arp.tmsk.s_addr)); printf("Interface: `%s'/", e->arp.iniface); for (i = 0; i < IFNAMSIZ; i++) printf("%c", e->arp.iniface_mask[i] ? 'X' : '.'); printf("to `%s'/", e->arp.outiface); for (i = 0; i < IFNAMSIZ; i++) printf("%c", e->arp.outiface_mask[i] ? 'X' : '.'); printf("Flags: %02X\n", e->arp.flags); printf("Invflags: %02X\n", e->arp.invflags); printf("Counters: %llu packets, %llu bytes\n", e->counters.pcnt, e->counters.bcnt); /* printf("Cache: %08X ", e->nfcache); if (e->nfcache & NFC_ALTERED) printf("ALTERED "); if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN "); if (e->nfcache & NFC_IP_SRC) printf("IP_SRC "); if (e->nfcache & NFC_IP_DST) printf("IP_DST "); if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN "); if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT "); if (e->nfcache & NFC_IP_TOS) printf("IP_TOS "); if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO "); if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS "); if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS "); if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT "); if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT "); if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN "); */ printf("\n"); /* ARPT_MATCH_ITERATE(e, print_match); */ t = GET_TARGET(e); printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size); if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) { int pos = *(int *)t->data; if (pos < 0) printf("verdict=%s\n", pos == -NF_ACCEPT-1 ? "NF_ACCEPT" : pos == -NF_DROP-1 ? "NF_DROP" : pos == -NF_QUEUE-1 ? "NF_QUEUE" : pos == RETURN ? "RETURN" : "UNKNOWN"); else printf("verdict=%u\n", pos); } else if (strcmp(t->u.user.name, ARPT_ERROR_TARGET) == 0) printf("error=`%s'\n", t->data); printf("\n"); return 0; } static int is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask) { unsigned int i; STRUCT_ENTRY_TARGET *ta, *tb; unsigned char *mptr; /* Always compare head structures: ignore mask here. */ if (a->arp.src.s_addr != b->arp.src.s_addr || a->arp.tgt.s_addr != b->arp.tgt.s_addr || a->arp.smsk.s_addr != b->arp.smsk.s_addr || a->arp.tmsk.s_addr != b->arp.tmsk.s_addr || a->arp.arhln != b->arp.arhln || a->arp.arhln_mask != b->arp.arhln_mask || a->arp.arpop != b->arp.arpop || a->arp.arpop_mask != b->arp.arpop_mask || a->arp.arhrd != b->arp.arhrd || a->arp.arhrd_mask != b->arp.arhrd_mask || a->arp.arpro != b->arp.arpro || a->arp.arpro_mask != b->arp.arpro_mask || a->arp.flags != b->arp.flags || a->arp.invflags != b->arp.invflags) return 0; for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++) { if (a->arp.src_devaddr.addr[i] != b->arp.src_devaddr.addr[i] || a->arp.src_devaddr.mask[i] != b->arp.src_devaddr.mask[i]) return 0; if (a->arp.tgt_devaddr.addr[i] != b->arp.tgt_devaddr.addr[i] || a->arp.tgt_devaddr.mask[i] != b->arp.tgt_devaddr.mask[i]) return 0; } for (i = 0; i < IFNAMSIZ; i++) { if (a->arp.iniface_mask[i] != b->arp.iniface_mask[i]) return 0; if ((a->arp.iniface[i] & a->arp.iniface_mask[i]) != (b->arp.iniface[i] & b->arp.iniface_mask[i])) return 0; if (a->arp.outiface_mask[i] != b->arp.outiface_mask[i]) return 0; if ((a->arp.outiface[i] & a->arp.outiface_mask[i]) != (b->arp.outiface[i] & b->arp.outiface_mask[i])) return 0; } if (/* a->nfcache != b->nfcache || */a->target_offset != b->target_offset || a->next_offset != b->next_offset) return 0; mptr = matchmask + sizeof(STRUCT_ENTRY); /* if (ARPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr)) return 0; */ ta = GET_TARGET((STRUCT_ENTRY *)a); tb = GET_TARGET((STRUCT_ENTRY *)b); if (ta->u.target_size != tb->u.target_size) return 0; if (strcmp(ta->u.user.name, tb->u.user.name) != 0) return 0; mptr += sizeof(*ta); if (target_different(ta->data, tb->data, ta->u.target_size - sizeof(*ta), mptr)) return 0; return 1; } /***************************** DEBUGGING ********************************/ static inline int unconditional(const struct arpt_arp *arp) { unsigned int i; for (i = 0; i < sizeof(*arp)/sizeof(u_int32_t); i++) if (((u_int32_t *)arp)[i]) return 0; return 1; } /* static inline int check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off) { assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH)); assert(ALIGN(m->u.match_size) == m->u.match_size); (*off) += m->u.match_size; return 0; } */ static inline int check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off, unsigned int user_offset, int *was_return, TC_HANDLE_T h) { unsigned int toff; STRUCT_STANDARD_TARGET *t; assert(e->target_offset >= sizeof(STRUCT_ENTRY)); assert(e->next_offset >= e->target_offset + sizeof(STRUCT_ENTRY_TARGET)); toff = sizeof(STRUCT_ENTRY); /* ARPT_MATCH_ITERATE(e, check_match, &toff); */ assert(toff == e->target_offset); t = (STRUCT_STANDARD_TARGET *) GET_TARGET((STRUCT_ENTRY *)e); /* next_offset will have to be multiple of entry alignment. */ assert(e->next_offset == ALIGN(e->next_offset)); assert(e->target_offset == ALIGN(e->target_offset)); assert(t->target.u.target_size == ALIGN(t->target.u.target_size)); assert(!TC_IS_CHAIN(t->target.u.user.name, h)); if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) { assert(t->target.u.target_size == ALIGN(sizeof(STRUCT_STANDARD_TARGET))); assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1 || t->verdict == RETURN || t->verdict < (int)h->entries.size); if (t->verdict >= 0) { STRUCT_ENTRY *te = get_entry(h, t->verdict); int idx; idx = entry2index(h, te); assert(strcmp(GET_TARGET(te)->u.user.name, ARPT_ERROR_TARGET) != 0); assert(te != e); /* Prior node must be error node, or this node. */ assert(t->verdict == entry2offset(h, e)+e->next_offset || strcmp(GET_TARGET(index2entry(h, idx-1)) ->u.user.name, ARPT_ERROR_TARGET) == 0); } if (t->verdict == RETURN && unconditional(&e->arp) && e->target_offset == sizeof(*e)) *was_return = 1; else *was_return = 0; } else if (strcmp(t->target.u.user.name, ARPT_ERROR_TARGET) == 0) { assert(t->target.u.target_size == ALIGN(sizeof(struct arpt_error_target))); /* If this is in user area, previous must have been return */ if (*off > user_offset) assert(*was_return); *was_return = 0; } else *was_return = 0; if (*off == user_offset) assert(strcmp(t->target.u.user.name, ARPT_ERROR_TARGET) == 0); (*off) += e->next_offset; (*i)++; return 0; } #ifdef ARPTC_DEBUG /* Do every conceivable sanity check on the handle */ static void do_check(TC_HANDLE_T h, unsigned int line) { unsigned int i, n; unsigned int user_offset; /* Offset of first user chain */ int was_return; assert(h->changed == 0 || h->changed == 1); if (strcmp(h->info.name, "filter") == 0) { assert(h->info.valid_hooks == (1 << NF_IP_LOCAL_IN | 1 << NF_IP_FORWARD | 1 << NF_IP_LOCAL_OUT)); /* Hooks should be first three */ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0); n = get_chain_end(h, 0); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_FORWARD] == n); n = get_chain_end(h, n); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; } else if (strcmp(h->info.name, "nat") == 0) { assert((h->info.valid_hooks == (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT)) || (h->info.valid_hooks == (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN | 1 << NF_IP_POST_ROUTING | 1 << NF_IP_LOCAL_OUT))); assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); n = get_chain_end(h, 0); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n); n = get_chain_end(h, n); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) { n = get_chain_end(h, n); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n); user_offset = h->info.hook_entry[NF_IP_LOCAL_IN]; } } else if (strcmp(h->info.name, "mangle") == 0) { /* This code is getting ugly because linux < 2.4.18-pre6 had * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks * */ assert((h->info.valid_hooks == (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_OUT)) || (h->info.valid_hooks == (1 << NF_IP_PRE_ROUTING | 1 << NF_IP_LOCAL_IN | 1 << NF_IP_FORWARD | 1 << NF_IP_LOCAL_OUT | 1 << NF_IP_POST_ROUTING))); /* Hooks should be first five */ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); n = get_chain_end(h, 0); if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) { n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n); n = get_chain_end(h, n); } if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) { n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_FORWARD] == n); n = get_chain_end(h, n); } n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n); user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) { n = get_chain_end(h, n); n += get_entry(h, n)->next_offset; assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n); user_offset = h->info.hook_entry[NF_IP_POST_ROUTING]; } #ifdef NF_IP_DROPPING } else if (strcmp(h->info.name, "drop") == 0) { assert(h->info.valid_hooks == (1 << NF_IP_DROPPING)); /* Hook should be first */ assert(h->info.hook_entry[NF_IP_DROPPING] == 0); user_offset = 0; #endif } else { fprintf(stderr, "Unknown table `%s'\n", h->info.name); abort(); } /* User chain == end of last builtin + policy entry */ user_offset = get_chain_end(h, user_offset); user_offset += get_entry(h, user_offset)->next_offset; /* Overflows should be end of entry chains, and unconditional policy nodes. */ for (i = 0; i < RUNTIME_NF_ARP_NUMHOOKS; i++) { STRUCT_ENTRY *e; STRUCT_STANDARD_TARGET *t; if (!(h->info.valid_hooks & (1 << i))) continue; assert(h->info.underflow[i] == get_chain_end(h, h->info.hook_entry[i])); e = get_entry(h, get_chain_end(h, h->info.hook_entry[i])); assert(unconditional(&e->ip)); assert(e->target_offset == sizeof(*e)); t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); assert(t->target.u.target_size == ALIGN(sizeof(*t))); assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t))); assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0); assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1); /* Hooks and underflows must be valid entries */ entry2index(h, get_entry(h, h->info.hook_entry[i])); entry2index(h, get_entry(h, h->info.underflow[i])); } assert(h->info.size >= h->info.num_entries * (sizeof(STRUCT_ENTRY) +sizeof(STRUCT_STANDARD_TARGET))); assert(h->entries.size >= (h->new_number * (sizeof(STRUCT_ENTRY) + sizeof(STRUCT_STANDARD_TARGET)))); assert(strcmp(h->info.name, h->entries.name) == 0); i = 0; n = 0; was_return = 0; /* Check all the entries. */ ENTRY_ITERATE(h->entries.entrytable, h->entries.size, check_entry, &i, &n, user_offset, &was_return, h); assert(i == h->new_number); assert(n == h->entries.size); /* Final entry must be error node */ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1)) ->u.user.name, ERROR_TARGET) == 0); } #endif /*ARPTC_DEBUG*/ arptables-v0.0.3-4/extensions/0000700000175000017500000000000011345216210016414 5ustar bdschuymbdschuymarptables-v0.0.3-4/extensions/arpt_standard.c0000600000175000017500000000247411345216210021417 0ustar bdschuymbdschuym/* Shared library add-on to arptables for standard target support. */ #include #include #include #include #include #include #include /* Function which prints out usage message. */ static void help(void) { printf( "Standard v%s options:\n" "(If target is DROP, ACCEPT, RETURN or nothing)\n", ARPTABLES_VERSION); } static struct option opts[] = { {0} }; /* Initialize the target. */ static void init(struct arpt_entry_target *t) { } /* Function which parses command options; returns true if it ate an option */ static int parse(int c, char **argv, int invert, unsigned int *flags, const struct arpt_entry *entry, struct arpt_entry_target **target) { return 0; } /* Final check; don't care. */ static void final_check(unsigned int flags) { } /* Saves the targinfo in parsable form to stdout. */ static void save(const struct arpt_arp *ip, const struct arpt_entry_target *target) { } static struct arptables_target standard = { NULL, "standard", ARPTABLES_VERSION, ARPT_ALIGN(sizeof(int)), ARPT_ALIGN(sizeof(int)), &help, &init, &parse, &final_check, NULL, /* print */ &save, opts }; static void _init(void) __attribute__ ((constructor)); static void _init(void) { register_target(&standard); } arptables-v0.0.3-4/extensions/arpt_mangle.c0000600000175000017500000001245511345216210021062 0ustar bdschuymbdschuym#include #include #include #include #include #include #include #include static void help(void) { printf( "mangle target v%s options:\n" "--mangle-ip-s IP address\n" "--mangle-ip-d IP address\n" "--mangle-mac-s MAC address\n" "--mangle-mac-d MAC address\n" "--mangle-target target (DROP, CONTINUE or ACCEPT -- default is ACCEPT)\n", ARPTABLES_VERSION); } #define MANGLE_IPS '1' #define MANGLE_IPT '2' #define MANGLE_DEVS '3' #define MANGLE_DEVT '4' #define MANGLE_TARGET '5' static struct option opts[] = { { "mangle-ip-s" , required_argument, 0, MANGLE_IPS }, { "mangle-ip-d" , required_argument, 0, MANGLE_IPT }, { "mangle-mac-s" , required_argument, 0, MANGLE_DEVS }, { "mangle-mac-d" , required_argument, 0, MANGLE_DEVT }, { "mangle-target" , required_argument, 0, MANGLE_TARGET }, {0} }; static void init(struct arpt_entry_target *t) { struct arpt_mangle *mangle = (struct arpt_mangle *) t->data; mangle->target = NF_ACCEPT; } static int parse(int c, char **argv, int invert, unsigned int *flags, const struct arpt_entry *e, struct arpt_entry_target **t) { struct arpt_mangle *mangle = (struct arpt_mangle *)(*t)->data; struct in_addr *ipaddr; struct ether_addr *macaddr; int ret = 1; switch (c) { case MANGLE_IPS: /* if (e->arp.arpln_mask == 0) exit_error(PARAMETER_PROBLEM, "no pln defined"); if (e->arp.invflags & ARPT_INV_ARPPLN) exit_error(PARAMETER_PROBLEM, "! pln not allowed for --mangle-ip-s"); */ /* if (e->arp.arpln != 4) exit_error(PARAMETER_PROBLEM, "only pln=4 supported"); */ { unsigned int nr; ipaddr = parse_hostnetwork(argv[optind-1], &nr); } mangle->u_s.src_ip.s_addr = ipaddr->s_addr; free(ipaddr); mangle->flags |= ARPT_MANGLE_SIP; break; case MANGLE_IPT: /* if (e->arp.arpln_mask == 0) exit_error(PARAMETER_PROBLEM, "no pln defined"); if (e->arp.invflags & ARPT_INV_ARPPLN) exit_error(PARAMETER_PROBLEM, "! pln not allowed for --mangle-ip-d"); */ /* if (e->arp.arpln != 4) exit_error(PARAMETER_PROBLEM, "only pln=4 supported"); */ { unsigned int nr; ipaddr = parse_hostnetwork(argv[optind-1], &nr); } mangle->u_t.tgt_ip.s_addr = ipaddr->s_addr; free(ipaddr); mangle->flags |= ARPT_MANGLE_TIP; break; case MANGLE_DEVS: if (e->arp.arhln_mask == 0) exit_error(PARAMETER_PROBLEM, "no --h-length defined"); if (e->arp.invflags & ARPT_INV_ARPHLN) exit_error(PARAMETER_PROBLEM, "! --h-length not allowed for " "--mangle-mac-s"); if (e->arp.arhln != 6) exit_error(PARAMETER_PROBLEM, "only --h-length 6 " "supported"); macaddr = ether_aton(argv[optind-1]); if (macaddr == NULL) exit_error(PARAMETER_PROBLEM, "invalid source MAC"); memcpy(mangle->src_devaddr, macaddr, e->arp.arhln); mangle->flags |= ARPT_MANGLE_SDEV; break; case MANGLE_DEVT: if (e->arp.arhln_mask == 0) exit_error(PARAMETER_PROBLEM, "no --h-length defined"); if (e->arp.invflags & ARPT_INV_ARPHLN) exit_error(PARAMETER_PROBLEM, "! hln not allowed for --mangle-mac-d"); if (e->arp.arhln != 6) exit_error(PARAMETER_PROBLEM, "only --h-length 6 " "supported"); macaddr = ether_aton(argv[optind-1]); if (macaddr == NULL) exit_error(PARAMETER_PROBLEM, "invalid target MAC"); memcpy(mangle->tgt_devaddr, macaddr, e->arp.arhln); mangle->flags |= ARPT_MANGLE_TDEV; break; case MANGLE_TARGET: if (!strcmp(argv[optind-1], "DROP")) mangle->target = NF_DROP; else if (!strcmp(argv[optind-1], "ACCEPT")) mangle->target = NF_ACCEPT; else if (!strcmp(argv[optind-1], "CONTINUE")) mangle->target = ARPT_CONTINUE; else exit_error(PARAMETER_PROBLEM, "bad target for " "--mangle-target"); break; default: ret = 0; } return ret; } static void final_check(unsigned int flags) { } static void print(const struct arpt_arp *ip, const struct arpt_entry_target *target, int numeric) { struct arpt_mangle *m = (struct arpt_mangle *)(target->data); char buf[100]; if (m->flags & ARPT_MANGLE_SIP) { if (numeric) sprintf(buf, "%s", addr_to_dotted(&(m->u_s.src_ip))); else sprintf(buf, "%s", addr_to_anyname(&(m->u_s.src_ip))); printf("--mangle-ip-s %s ", buf); } if (m->flags & ARPT_MANGLE_SDEV) { printf("--mangle-mac-s "); print_mac((unsigned char *)m->src_devaddr, 6); printf(" "); } if (m->flags & ARPT_MANGLE_TIP) { if (numeric) sprintf(buf, "%s", addr_to_dotted(&(m->u_t.tgt_ip))); else sprintf(buf, "%s", addr_to_anyname(&(m->u_t.tgt_ip))); printf("--mangle-ip-d %s ", buf); } if (m->flags & ARPT_MANGLE_TDEV) { printf("--mangle-mac-d "); print_mac((unsigned char *)m->tgt_devaddr, 6); printf(" "); } if (m->target != NF_ACCEPT) { printf("--mangle-target "); if (m->target == NF_DROP) printf("DROP "); else printf("CONTINUE "); } } static void save(const struct arpt_arp *ip, const struct arpt_entry_target *target) { } static struct arptables_target change = { NULL, "mangle", ARPTABLES_VERSION, ARPT_ALIGN(sizeof(struct arpt_mangle)), ARPT_ALIGN(sizeof(struct arpt_mangle)), &help, &init, &parse, &final_check, &print, &save, opts }; static void _init(void) __attribute__ ((constructor)); static void _init(void) { register_target(&change); } arptables-v0.0.3-4/extensions/Makefile0000600000175000017500000000035111345216210020055 0ustar bdschuymbdschuym#! /usr/bin/make EXT_FUNC+=standard mangle EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/arpt_$(T).o) extensions/ebt_%.o: extensions/arpt_%.c include/arptables.h include/arptables_common.h $(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< arptables-v0.0.3-4/arptables-save0000600000175000017500000000255511345216210017062 0ustar bdschuymbdschuym#!/usr/bin/perl -w # # # A script that generates text output of the arptables rules. # Similar to iptables-save. use strict; my $table; my $tool = "__EXEC_PATH__/arptables"; # ======================================================== # Process filter table # ======================================================== sub process_table { my $chain = ""; my $rules = ""; my $chains = ""; my $custom_chains = ""; my $line = ""; foreach $line (split("\n",$_[0])) { if ($line =~ m/Chain\s(.*?)\s\(policy\s(.*?)\s/) { $chains = $chains . ":$1 $2\n"; $chain = $1; next; } if ($line =~ m/Chain\s(.*?)\s\(/) { $custom_chains = $custom_chains . ":$1 -\n"; $chain = $1; next; } if ($line =~ m/^$/) { next; } # Due to arptables "issues" with displaying device names # we need to use -v and then do some processing $line =~ s/\s,\s.*//; $rules = $rules . "-A $chain $line\n"; } print "*filter\n"; print $chains; print $custom_chains; print $rules; print "\n"; } # ======================================================== unless (-x "$tool") { print "ERROR: Tool $tool isn't executable"; exit -1; }; $table =`$tool -t filter -L -v`; unless ($? == 0) { print $table; exit -1 }; &process_table($table);