From owner-freebsd-ipfw@FreeBSD.ORG Sun Dec 25 15:00:35 2011 Return-Path: Delivered-To: freebsd-ipfw@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D6032106566B; Sun, 25 Dec 2011 15:00:35 +0000 (UTC) (envelope-from melifaro@FreeBSD.org) Received: from mail.ipfw.ru (unknown [IPv6:2a01:4f8:120:6141::2]) by mx1.freebsd.org (Postfix) with ESMTP id 49B948FC12; Sun, 25 Dec 2011 15:00:34 +0000 (UTC) Received: from secured.by.ipfw.ru ([81.200.11.182] helo=ws.su29.net) by mail.ipfw.ru with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.76 (FreeBSD)) (envelope-from ) id 1RepYq-000HGF-QA; Sun, 25 Dec 2011 19:00:29 +0400 Message-ID: <4EF73A4A.3050902@FreeBSD.org> Date: Sun, 25 Dec 2011 18:59:22 +0400 From: "Alexander V. Chernikov" User-Agent: Thunderbird 2.0.0.24 (X11/20100515) MIME-Version: 1.0 To: Jason Hellenthal References: <1674097252.20111218125051@nitronet.pl> <4EEDD566.8020609@FreeBSD.org> <20111220163355.GA87584@DataIX.net> In-Reply-To: <20111220163355.GA87584@DataIX.net> X-Enigmail-Version: 0.96.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="------------enigABFE081192EEA4308BF10E2C" Cc: Pawel Tyll , freebsd-net@freebsd.org, "Andrey V. Elsukov" , freebsd-ipfw@freebsd.org Subject: IPFW eXtended tables [Was: Re: IPFW tables, dummynet and IPv6] X-BeenThere: freebsd-ipfw@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: IPFW Technical Discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 25 Dec 2011 15:00:35 -0000 This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enigABFE081192EEA4308BF10E2C Content-Type: multipart/mixed; boundary="------------030403040904080105030905" This is a multi-part message in MIME format. --------------030403040904080105030905 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Hello everyone. Final patch version now uses single IP_FW3 socket option. Together with other changes this makes me think such changes should be reviewed by a wider number of people. If there are no objections/comments I plan to commit this on tuesday. Changes: * Tables (actually, radix trees) are now created/freed on demand. * Tables can be of different types (CIDR and interfaces are supported at the moment) * Each tables has 2 pointers (basic and eXtended tree) which are initialized independently permitting both IPv4/IPv6 address to be specified in the same table without performance loss * Every new opcode uses IP_FW3 socket option This change does not break ABI, old ipfw(8) binary can configure IPv4 addresses on CIDR-type tables and flush every table. --------------030403040904080105030905 Content-Type: text/plain; name="ipfw_tables8.diff" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline; filename="ipfw_tables8.diff" Index: sbin/ipfw/ipfw2.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sbin/ipfw/ipfw2.c (revision 228874) +++ sbin/ipfw/ipfw2.c (working copy) @@ -42,6 +42,8 @@ #include /* _long_to_time */ #include #include +#include /* MIN */ +#include /* offsetof */ =20 #include #include /* only IFNAMSIZ */ @@ -57,6 +59,12 @@ struct cmdline_opts co; /* global options */ =20 int resvd_set_number =3D RESVD_SET; =20 +int ipfw_socket =3D -1; + +#ifndef s6_addr32 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ @@ -370,33 +378,65 @@ safe_realloc(void *ptr, size_t size) int do_cmd(int optname, void *optval, uintptr_t optlen) { - static int s =3D -1; /* the socket */ int i; =20 if (co.test_only) return 0; =20 - if (s =3D=3D -1) - s =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (s < 0) + if (ipfw_socket =3D=3D -1) + ipfw_socket =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); =20 if (optname =3D=3D IP_FW_GET || optname =3D=3D IP_DUMMYNET_GET || - optname =3D=3D IP_FW_ADD || optname =3D=3D IP_FW_TABLE_LIST || - optname =3D=3D IP_FW_TABLE_GETSIZE || + optname =3D=3D IP_FW_ADD || optname =3D=3D IP_FW3 || optname =3D=3D IP_FW_NAT_GET_CONFIG || optname < 0 || optname =3D=3D IP_FW_NAT_GET_LOG) { if (optname < 0) optname =3D -optname; - i =3D getsockopt(s, IPPROTO_IP, optname, optval, + i =3D getsockopt(ipfw_socket, IPPROTO_IP, optname, optval, (socklen_t *)optlen); } else { - i =3D setsockopt(s, IPPROTO_IP, optname, optval, optlen); + i =3D setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen); } return i; } =20 +/* + * do_setcmd3 - pass ipfw control cmd to kernel + * @optname: option name + * @optval: pointer to option data + * @optlen: option length + * + * Function encapsulates option value in IP_FW3 socket option + * and calls setsockopt(). + * Function returns 0 on success or -1 overwise. + */ +int +do_setcmd3(int optname, void *optval, socklen_t optlen) +{ + socklen_t len; + ip_fw3_opheader *op3; + + if (co.test_only) + return (0); + + if (ipfw_socket =3D=3D -1) + ipfw_socket =3D socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (ipfw_socket < 0) + err(EX_UNAVAILABLE, "socket"); + + len =3D sizeof(ip_fw3_opheader) + optlen; + op3 =3D alloca(len); + /* Zero reserved fields */ + memset(op3, 0, sizeof(ip_fw3_opheader)); + memcpy(op3 + 1, optval, optlen); + op3->opcode =3D optname; + + return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len); +} + /** * match_token takes a table and a string, returns the value associated * with the string (-1 in case of failure). @@ -3854,7 +3894,7 @@ ipfw_flush(int force) } =20 =20 -static void table_list(ipfw_table_entry ent, int need_header); +static void table_list(uint16_t num, int need_header); =20 /* * This one handles all table-related commands @@ -3866,12 +3906,12 @@ ipfw_flush(int force) void ipfw_table_handler(int ac, char *av[]) { - ipfw_table_entry ent; + ipfw_table_xentry xent; int do_add; int is_all; size_t len; char *p; - uint32_t a; + uint32_t a, type, mask, addrlen; uint32_t tables_max; =20 len =3D sizeof(tables_max); @@ -3886,18 +3926,20 @@ ipfw_table_handler(int ac, char *av[]) #endif } =20 + memset(&xent, 0, sizeof(xent)); + ac--; av++; if (ac && isdigit(**av)) { - ent.tbl =3D atoi(*av); + xent.tbl =3D atoi(*av); is_all =3D 0; ac--; av++; } else if (ac && _substrcmp(*av, "all") =3D=3D 0) { - ent.tbl =3D 0; + xent.tbl =3D 0; is_all =3D 1; ac--; av++; } else errx(EX_USAGE, "table number or 'all' keyword required"); - if (ent.tbl >=3D tables_max) + if (xent.tbl >=3D tables_max) errx(EX_USAGE, "The table number exceeds the maximum allowed " "value (%d)", tables_max - 1); NEED1("table needs command"); @@ -3910,104 +3952,181 @@ ipfw_table_handler(int ac, char *av[]) do_add =3D **av =3D=3D 'a'; ac--; av++; if (!ac) - errx(EX_USAGE, "IP address required"); - p =3D strchr(*av, '/'); - if (p) { - *p++ =3D '\0'; - ent.masklen =3D atoi(p); - if (ent.masklen > 32) - errx(EX_DATAERR, "bad width ``%s''", p); - } else - ent.masklen =3D 32; - if (lookup_host(*av, (struct in_addr *)&ent.addr) !=3D 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); + errx(EX_USAGE, "address required"); + /*=20 + * Let's try to guess type by agrument. + * Possible types:=20 + * 1) IPv4[/mask] + * 2) IPv6[/mask] + * 3) interface name + * 4) port ? + */ + type =3D 0; + if (ishexnumber(*av[0])) { + /* Remove / if exists */ + if ((p =3D strchr(*av, '/')) !=3D NULL) { + *p =3D '\0'; + mask =3D atoi(p + 1); + } + + if (inet_pton(AF_INET, *av, &xent.k.addr6) =3D=3D 1) { + type =3D IPFW_TABLE_CIDR; + if ((p !=3D NULL) && (mask > 32)) + errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); + xent.masklen =3D p ? mask : 32; + addrlen =3D sizeof(struct in_addr); + } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) =3D=3D 1) { + type =3D IPFW_TABLE_CIDR; + if ((p !=3D NULL) && (mask > 128)) + errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); + xent.masklen =3D p ? mask : 128; + addrlen =3D sizeof(struct in6_addr); + } + } + + if ((type =3D=3D 0) && (strchr(*av, '.') =3D=3D NULL)) { + /* Assume interface name. Copy significant data only */ + mask =3D MIN(strlen(*av), IF_NAMESIZE - 1); + memcpy(xent.k.iface, *av, mask); + /* Set mask to exact match */ + xent.masklen =3D 8 * IF_NAMESIZE; + type =3D IPFW_TABLE_INTERFACE; + addrlen =3D IF_NAMESIZE; + } + + if (type =3D=3D 0) { + if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) !=3D 0) + errx(EX_NOHOST, "hostname ``%s'' unknown", *av); + xent.masklen =3D 32; + type =3D IPFW_TABLE_CIDR; + addrlen =3D sizeof(struct in_addr); + } + + xent.type =3D type; + xent.len =3D offsetof(ipfw_table_xentry, k) + addrlen; + ac--; av++; if (do_add && ac) { unsigned int tval; /* isdigit is a bit of a hack here.. */ if (strchr(*av, (int)'.') =3D=3D NULL && isdigit(**av)) { - ent.value =3D strtoul(*av, NULL, 0); + xent.value =3D strtoul(*av, NULL, 0); } else { if (lookup_host(*av, (struct in_addr *)&tval) =3D=3D 0) { /* The value must be stored in host order * * so that the values < 65k can be distinguished */ - ent.value =3D ntohl(tval); + xent.value =3D ntohl(tval); } else { errx(EX_NOHOST, "hostname ``%s'' unknown", *av); } } } else - ent.value =3D 0; - if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL, - &ent, sizeof(ent)) < 0) { + xent.value =3D 0; + if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL, + &xent, xent.len) < 0) { /* If running silent, don't bomb out on these errors. */ if (!(co.do_quiet && (errno =3D=3D (do_add ? EEXIST : ESRCH)))) err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)", - do_add ? "ADD" : "DEL"); + do_add ? "XADD" : "XDEL"); /* In silent mode, react to a failed add by deleting */ if (do_add) { - do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent)); - if (do_cmd(IP_FW_TABLE_ADD, - &ent, sizeof(ent)) < 0) + do_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len); + if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0) err(EX_OSERR, - "setsockopt(IP_FW_TABLE_ADD)"); + "setsockopt(IP_FW_TABLE_XADD)"); } } } else if (_substrcmp(*av, "flush") =3D=3D 0) { - a =3D is_all ? tables_max : (uint32_t)(ent.tbl + 1); + a =3D is_all ? tables_max : (uint32_t)(xent.tbl + 1); do { - if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, - sizeof(ent.tbl)) < 0) + if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl, + sizeof(xent.tbl)) < 0) err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)"); - } while (++ent.tbl < a); + } while (++xent.tbl < a); } else if (_substrcmp(*av, "list") =3D=3D 0) { - a =3D is_all ? tables_max : (uint32_t)(ent.tbl + 1); + a =3D is_all ? tables_max : (uint32_t)(xent.tbl + 1); do { - table_list(ent, is_all); - } while (++ent.tbl < a); + table_list(xent.tbl, is_all); + } while (++xent.tbl < a); } else errx(EX_USAGE, "invalid table command %s", *av); } =20 static void -table_list(ipfw_table_entry ent, int need_header) +table_list(uint16_t num, int need_header) { - ipfw_table *tbl; + ipfw_xtable *tbl; + ipfw_table_xentry *xent; socklen_t l; - uint32_t a; + uint32_t *a, sz, tval; + char tbuf[128]; + struct in6_addr *addr6; + ip_fw3_opheader *op3; =20 - a =3D ent.tbl; - l =3D sizeof(a); - if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0) - err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)"); + /* Prepend value with IP_FW3 header */ + l =3D sizeof(ip_fw3_opheader) + sizeof(uint32_t); + op3 =3D alloca(l); + /* Zero reserved fields */ + memset(op3, 0, sizeof(ip_fw3_opheader)); + a =3D (uint32_t *)(op3 + 1); + *a =3D num; + op3->opcode =3D IP_FW_TABLE_XGETSIZE; + if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0) + err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)"); =20 /* If a is zero we have nothing to do, the table is empty. */ - if (a =3D=3D 0) + if (*a =3D=3D 0) return; =20 - l =3D sizeof(*tbl) + a * sizeof(ipfw_table_entry); + l =3D *a; tbl =3D safe_calloc(1, l); - tbl->tbl =3D ent.tbl; - if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0) - err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)"); + tbl->opheader.opcode =3D IP_FW_TABLE_XLIST; + tbl->tbl =3D num; + if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0) + err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)"); if (tbl->cnt && need_header) printf("---table(%d)---\n", tbl->tbl); - for (a =3D 0; a < tbl->cnt; a++) { - unsigned int tval; - tval =3D tbl->ent[a].value; - if (co.do_value_as_ip) { - char tbuf[128]; - strncpy(tbuf, inet_ntoa(*(struct in_addr *) - &tbl->ent[a].addr), 127); - /* inet_ntoa expects network order */ - tval =3D htonl(tval); - printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen, - inet_ntoa(*(struct in_addr *)&tval)); - } else { - printf("%s/%u %u\n", - inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr), - tbl->ent[a].masklen, tval); + sz =3D tbl->size - sizeof(ipfw_xtable); + xent =3D &tbl->xent[0]; + while (sz > 0) { + switch (tbl->type) { + case IPFW_TABLE_CIDR: + /* IPv4 or IPv6 prefixes */ + tval =3D xent->value; + addr6 =3D &xent->k.addr6; + + if ((addr6->s6_addr32[0] =3D=3D 0) && (addr6->s6_addr32[1] =3D=3D 0) = &&=20 + (addr6->s6_addr32[2] =3D=3D 0)) { + /* IPv4 address */ + inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); + } else { + /* IPv6 address */ + inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf)); + } + + if (co.do_value_as_ip) { + tval =3D htonl(tval); + printf("%s/%u %s\n", tbuf, xent->masklen, + inet_ntoa(*(struct in_addr *)&tval)); + } else + printf("%s/%u %u\n", tbuf, xent->masklen, tval); + break; + case IPFW_TABLE_INTERFACE: + /* Interface names, direct match at the moment */ + tval =3D xent->value; + if (co.do_value_as_ip) { + tval =3D htonl(tval); + printf("%s/%s %s\n", xent->k.iface, xent->k.iface, + inet_ntoa(*(struct in_addr *)&tval)); + } else + printf("%s/%s %u\n", xent->k.iface, xent->k.iface, tval); } + + if (sz < xent->len) + break; + sz -=3D xent->len; + xent =3D (void *)xent + xent->len; } + free(tbl); } Index: sys/netinet/ip_fw.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sys/netinet/ip_fw.h (revision 228874) +++ sys/netinet/ip_fw.h (working copy) @@ -62,6 +62,19 @@ */ #define IPFW_CALLSTACK_SIZE 16 =20 +/* IP_FW3 header/opcodes */ +typedef struct _ip_fw3_opheader { + uint16_t opcode; /* Operation opcode */ + uint16_t reserved[3]; /* Align to 64-bit boundary */ +} ip_fw3_opheader; + + +/* IPFW extented tables support */ +#define IP_FW_TABLE_XADD 86 /* add entry */ +#define IP_FW_TABLE_XDEL 87 /* delete entry */ +#define IP_FW_TABLE_XGETSIZE 88 /* get table size */ +#define IP_FW_TABLE_XLIST 89 /* list table contents */ + /* * The kernel representation of ipfw rules is made of a list of * 'instructions' (for all practical purposes equivalent to BPF @@ -581,6 +594,11 @@ struct _ipfw_dyn_rule { /* * These are used for lookup tables. */ + +#define IPFW_TABLE_CIDR 1 /* Table for holding IPv4/IPv6 prefixes */ +#define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */ +#define IPFW_TABLE_MAXTYPE 2 /* Maximum valid number */ + typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ u_int32_t value; /* value */ @@ -588,6 +606,19 @@ typedef struct _ipfw_table_entry { u_int8_t masklen; /* mask length */ } ipfw_table_entry; =20 +typedef struct _ipfw_table_xentry { + uint16_t len; /* Total entry length */ + uint8_t type; /* entry type */ + uint8_t masklen; /* mask length */ + uint16_t tbl; /* table number */ + uint32_t value; /* value */ + union { + /* Longest field needs to be aligned by 4-byte boundary */ + struct in6_addr addr6; /* IPv6 address */ + char iface[IF_NAMESIZE]; /* interface name */ + } k; +} ipfw_table_xentry; + typedef struct _ipfw_table { u_int32_t size; /* size of entries in bytes */ u_int32_t cnt; /* # of entries */ @@ -595,4 +626,13 @@ typedef struct _ipfw_table { ipfw_table_entry ent[0]; /* entries */ } ipfw_table; =20 +typedef struct _ipfw_xtable { + ip_fw3_opheader opheader; /* eXtended tables are controlled via IP_FW3 = */ + uint32_t size; /* size of entries in bytes */ + uint32_t cnt; /* # of entries */ + uint16_t tbl; /* table number */ + uint8_t type; /* table type */ + ipfw_table_xentry xent[0]; /* entries */ +} ipfw_xtable; + #endif /* _IPFW2_H */ Index: sys/netinet/ipfw/ip_fw_private.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sys/netinet/ipfw/ip_fw_private.h (revision 228874) +++ sys/netinet/ipfw/ip_fw_private.h (working copy) @@ -216,8 +216,6 @@ struct ip_fw_chain { int n_rules; /* number of static rules */ int static_len; /* total len of static rules */ struct ip_fw **map; /* array of rule ptrs to ease lookup */ - LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ - struct radix_node_head *tables[IPFW_TABLES_MAX]; #if defined( __linux__ ) || defined( _WIN32 ) spinlock_t rwmtx; spinlock_t uh_lock; @@ -227,6 +225,10 @@ struct ip_fw_chain { #endif uint32_t id; /* ruleset id */ uint32_t gencnt; /* generation count */ + LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */ + struct radix_node_head *tables[IPFW_TABLES_MAX]; /* IPv4 tables */ + struct radix_node_head *xtables[IPFW_TABLES_MAX]; /* extended tables */= + uint8_t tabletype[IPFW_TABLES_MAX]; /* Table type */ }; =20 struct sockopt; /* used by tcp_var.h */ @@ -273,16 +275,20 @@ int ipfw_check_hook(void *arg, struct mbuf **m0, s struct radix_node; int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t ad= dr, uint32_t *val); +int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, voi= d *paddr, + uint32_t *val, int type); int ipfw_init_tables(struct ip_fw_chain *ch); void ipfw_destroy_tables(struct ip_fw_chain *ch); int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl); -int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t= addr, - uint8_t mlen, uint32_t value); +int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *pad= dr, + uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value); +int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *pad= dr, + uint8_t plen, uint8_t mlen, uint8_t type); +int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt= ); int ipfw_dump_table_entry(struct radix_node *rn, void *arg); -int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t= addr, - uint8_t mlen); -int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt= ); int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl); +int ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cn= t); +int ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl); =20 /* In ip_fw_nat.c -- XXX to be moved to ip_var.h */ =20 Index: sys/netinet/ipfw/ip_fw_sockopt.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sys/netinet/ipfw/ip_fw_sockopt.c (revision 228874) +++ sys/netinet/ipfw/ip_fw_sockopt.c (working copy) @@ -668,7 +668,6 @@ check_ipfw_struct(struct ip_fw *rule, int size) cmdlen !=3D F_INSN_SIZE(ipfw_insn_u32)) goto bad_size; break; - case O_MACADDR2: if (cmdlen !=3D F_INSN_SIZE(ipfw_insn_mac)) goto bad_size; @@ -941,6 +940,7 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf } =20 =20 +#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader))= /** * {set|get}sockopt parser. */ @@ -949,10 +949,13 @@ ipfw_ctl(struct sockopt *sopt) { #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error; - size_t size; + size_t size, len, valsize; struct ip_fw *buf, *rule; struct ip_fw_chain *chain; u_int32_t rulenum[2]; + uint32_t opt; + char xbuf[128]; + ip_fw3_opheader *op3 =3D NULL; =20 error =3D priv_check(sopt->sopt_td, PRIV_NETINET_IPFW); if (error) @@ -972,7 +975,17 @@ ipfw_ctl(struct sockopt *sopt) chain =3D &V_layer3_chain; error =3D 0; =20 - switch (sopt->sopt_name) { + /* Save original valsize before it is altered via sooptcopyin() */ + valsize =3D sopt->sopt_valsize; + if ((opt =3D sopt->sopt_name) =3D=3D IP_FW3) { + if ((error =3D sooptcopyin(sopt, xbuf, sizeof(xbuf), + sizeof(ip_fw3_opheader))) !=3D 0) + return (error); + op3 =3D (ip_fw3_opheader *)xbuf; + opt =3D op3->opcode; + } + + switch (opt) { case IP_FW_GET: /* * pass up a copy of the current rules. Static rules @@ -1111,7 +1124,8 @@ ipfw_ctl(struct sockopt *sopt) if (error) break; error =3D ipfw_add_table_entry(chain, ent.tbl, - ent.addr, ent.masklen, ent.value); + &ent.addr, sizeof(ent.addr), ent.masklen,=20 + IPFW_TABLE_CIDR, ent.value); } break; =20 @@ -1124,10 +1138,36 @@ ipfw_ctl(struct sockopt *sopt) if (error) break; error =3D ipfw_del_table_entry(chain, ent.tbl, - ent.addr, ent.masklen); + &ent.addr, sizeof(ent.addr), ent.masklen, IPFW_TABLE_CIDR); } break; =20 + case IP_FW_TABLE_XADD: /* IP_FW3 */ + case IP_FW_TABLE_XDEL: /* IP_FW3 */ + { + ipfw_table_xentry *xent =3D (ipfw_table_xentry *)(op3 + 1); + + /* Check minimum header size */ + if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { + error =3D EINVAL; + break; + } + + /* Check if len field is valid */ + if ((len =3D xent->len - offsetof(ipfw_table_xentry, k)) >=20 + sizeof(ipfw_table_xentry)) { + error =3D EINVAL; + break; + } + + error =3D (opt =3D=3D IP_FW_TABLE_XADD) ? + ipfw_add_table_entry(chain, xent->tbl, &xent->k,=20 + len, xent->masklen, xent->type, xent->value) : + ipfw_del_table_entry(chain, xent->tbl, &xent->k, + len, xent->masklen, xent->type); + } + break; + case IP_FW_TABLE_FLUSH: { u_int16_t tbl; @@ -1136,9 +1176,7 @@ ipfw_ctl(struct sockopt *sopt) sizeof(tbl), sizeof(tbl)); if (error) break; - IPFW_WLOCK(chain); error =3D ipfw_flush_table(chain, tbl); - IPFW_WUNLOCK(chain); } break; =20 @@ -1187,6 +1225,62 @@ ipfw_ctl(struct sockopt *sopt) } break; =20 + case IP_FW_TABLE_XGETSIZE: /* IP_FW3 */ + { + uint32_t *tbl; + + if (IP_FW3_OPLENGTH(sopt) < sizeof(uint32_t)) { + error =3D EINVAL; + break; + } + + tbl =3D (uint32_t *)(op3 + 1); + + IPFW_RLOCK(chain); + error =3D ipfw_count_xtable(chain, *tbl, tbl); + IPFW_RUNLOCK(chain); + if (error) + break; + error =3D sooptcopyout(sopt, op3, sopt->sopt_valsize); + } + break; + + case IP_FW_TABLE_XLIST: /* IP_FW3 */ + { + ipfw_xtable *tbl; + + if ((size =3D valsize) < sizeof(ipfw_xtable)) { + error =3D EINVAL; + break; + } + + tbl =3D malloc(size, M_TEMP, M_ZERO | M_WAITOK); + memcpy(tbl, op3, sizeof(ipfw_xtable)); + + /* Get maximum number of entries we can store */ + tbl->size =3D (size - sizeof(ipfw_xtable)) / + sizeof(ipfw_table_xentry); + IPFW_RLOCK(chain); + error =3D ipfw_dump_xtable(chain, tbl); + IPFW_RUNLOCK(chain); + if (error) { + free(tbl, M_TEMP); + break; + } + + /* Revert size field back to bytes */ + tbl->size =3D tbl->size * sizeof(ipfw_table_xentry) + + sizeof(ipfw_table); + /*=20 + * Since we call sooptcopyin() with small buffer, sopt_valsize is + * decreased to reflect supplied buffer size. Set it back to original= value + */ + sopt->sopt_valsize =3D valsize; + error =3D sooptcopyout(sopt, tbl, size); + free(tbl, M_TEMP); + } + break; + /*--- NAT operations are protected by the IPFW_LOCK ---*/ case IP_FW_NAT_CFG: if (IPFW_NAT_LOADED) Index: sys/netinet/ipfw/ip_fw_table.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sys/netinet/ipfw/ip_fw_table.c (revision 228874) +++ sys/netinet/ipfw/ip_fw_table.c (working copy) @@ -76,6 +76,29 @@ struct table_entry { u_int32_t value; }; =20 +struct xaddr_iface { + uint8_t if_len; /* length of this struct */ + uint8_t pad[7]; /* Align name */ + char ifname[IF_NAMESIZE]; /* Interface name */ +}; + +struct table_xentry { + struct radix_node rn[2]; + union { +#ifdef INET6 + struct sockaddr_in6 addr6; +#endif + struct xaddr_iface iface; + } a; + union { +#ifdef INET6 + struct sockaddr_in6 mask6; +#endif + struct xaddr_iface ifmask; + } m; + u_int32_t value; +}; + /* * The radix code expects addr and mask to be array of bytes, * with the first byte being the length of the array. rn_inithead @@ -87,57 +110,264 @@ struct table_entry { */ #define KEY_LEN(v) *((uint8_t *)&(v)) #define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) +/* + * Do not require radix to compare more than actual IPv4/IPv6 address + */ +#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in= _addr_t)) +#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof= (struct in6_addr)) =20 +static inline void +ipv6_writemask(struct in6_addr *addr6, uint8_t mask) +{ + uint32_t *cp; + + for (cp =3D (uint32_t *)addr6; mask >=3D 32; mask -=3D 32) + *cp++ =3D 0xFFFFFFFF; + *cp =3D htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); +} + int -ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t add= r, - uint8_t mlen, uint32_t value) +ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) { - struct radix_node_head *rnh; + struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; + struct table_xentry *xent; struct radix_node *rn; + in_addr_t addr; + int offset; + void *ent_ptr; + struct sockaddr *addr_ptr, *mask_ptr; + char c; =20 if (tbl >=3D IPFW_TABLES_MAX) return (EINVAL); - rnh =3D ch->tables[tbl]; - ent =3D malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); - if (ent =3D=3D NULL) - return (ENOMEM); - ent->value =3D value; - KEY_LEN(ent->addr) =3D KEY_LEN(ent->mask) =3D 8; - ent->mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : = 0); - ent->addr.sin_addr.s_addr =3D addr & ent->mask.sin_addr.s_addr; + + switch (type) { + case IPFW_TABLE_CIDR: + if (plen =3D=3D sizeof(in_addr_t)) { + ent =3D malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); + ent->value =3D value; + /* Set 'total' structure length */ + KEY_LEN(ent->addr) =3D KEY_LEN_INET; + KEY_LEN(ent->mask) =3D KEY_LEN_INET; + /* Set offset of IPv4 address in bits */ + offset =3D (8 * offsetof(struct sockaddr_in, sin_addr)); + ent->mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) = : 0); + addr =3D *((in_addr_t *)paddr); + ent->addr.sin_addr.s_addr =3D addr & ent->mask.sin_addr.s_addr; + /* Set pointers */ + rnh_ptr =3D &ch->tables[tbl]; + ent_ptr =3D ent; + addr_ptr =3D (struct sockaddr *)&ent->addr; + mask_ptr =3D (struct sockaddr *)&ent->mask; +#ifdef INET6 + } else if (plen =3D=3D sizeof(struct in6_addr)) { + /* IPv6 case */ + if (mlen > 128) + return (EINVAL); + xent =3D malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value =3D value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.addr6) =3D KEY_LEN_INET6; + KEY_LEN(xent->m.mask6) =3D KEY_LEN_INET6; + /* Set offset of IPv6 address in bits */ + offset =3D (8 * offsetof(struct sockaddr_in6, sin6_addr)); + ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); + memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); + APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); + /* Set pointers */ + rnh_ptr =3D &ch->xtables[tbl]; + ent_ptr =3D xent; + addr_ptr =3D (struct sockaddr *)&xent->a.addr6; + mask_ptr =3D (struct sockaddr *)&xent->m.mask6; +#endif + } else { + /* Unknown CIDR type */ + return (EINVAL); + } + break; +=09 + case IPFW_TABLE_INTERFACE: + /* Check if string is terminated */ + c =3D ((char *)paddr)[IF_NAMESIZE - 1]; + ((char *)paddr)[IF_NAMESIZE - 1] =3D '\0'; + if (((mlen =3D strlen((char *)paddr)) =3D=3D IF_NAMESIZE - 1) && (c !=3D= '\0')) + return (EINVAL); + + xent =3D malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); + xent->value =3D value; + /* Set 'total' structure length */ + KEY_LEN(xent->a.iface) =3D mlen; + KEY_LEN(xent->m.ifmask) =3D mlen; + /* Set offset of interface name in bits */ + offset =3D (8 * offsetof(struct xaddr_iface, ifname)); + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ +#if 0 + memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); + mask_ptr =3D (struct sockaddr *)&xent->m.ifmask; +#endif + mask_ptr =3D NULL; + memcpy(xent->a.iface.ifname, paddr, mlen); + /* Set pointers */ + rnh_ptr =3D &ch->xtables[tbl]; + ent_ptr =3D xent; + addr_ptr =3D (struct sockaddr *)&xent->a.iface; + break; + + default: + return (EINVAL); + } + IPFW_WLOCK(ch); - rn =3D rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); + + /* Check if tabletype is valid */ + if ((ch->tabletype[tbl] !=3D 0) && (ch->tabletype[tbl] !=3D type)) { + IPFW_WUNLOCK(ch); + free(ent_ptr, M_IPFW_TBL); + return (EINVAL); + } + + /* Check if radix tree exists */ + if ((rnh =3D *rnh_ptr) =3D=3D NULL) { + IPFW_WUNLOCK(ch); + /* Create radix for a new table */ + if (!rn_inithead((void **)&rnh, offset)) { + free(ent_ptr, M_IPFW_TBL); + return (ENOMEM); + } + + IPFW_WLOCK(ch); + if (*rnh_ptr !=3D NULL) { + /* Tree is already attached by other thread */ + rn_detachhead((void **)&rnh); + rnh =3D *rnh_ptr; + /* Check table type another time */ + if (ch->tabletype[tbl] !=3D type) { + IPFW_WUNLOCK(ch); + free(ent_ptr, M_IPFW_TBL); + return (EINVAL); + } + } else { + *rnh_ptr =3D rnh; + /*=20 + * Set table type. It can be set already + * (if we have IPv6-only table) but setting + * it another time does not hurt + */ + ch->tabletype[tbl] =3D type; + } + } + + rn =3D rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); + IPFW_WUNLOCK(ch); + if (rn =3D=3D NULL) { - IPFW_WUNLOCK(ch); - free(ent, M_IPFW_TBL); + free(ent_ptr, M_IPFW_TBL); return (EEXIST); } - IPFW_WUNLOCK(ch); return (0); } =20 int -ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t add= r, - uint8_t mlen) +ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, + uint8_t plen, uint8_t mlen, uint8_t type) { - struct radix_node_head *rnh; + struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; + in_addr_t addr; struct sockaddr_in sa, mask; + struct sockaddr *sa_ptr, *mask_ptr; + char c; =20 if (tbl >=3D IPFW_TABLES_MAX) return (EINVAL); - rnh =3D ch->tables[tbl]; - KEY_LEN(sa) =3D KEY_LEN(mask) =3D 8; - mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); - sa.sin_addr.s_addr =3D addr & mask.sin_addr.s_addr; + + switch (type) { + case IPFW_TABLE_CIDR: + if (plen =3D=3D sizeof(in_addr_t)) { + /* Set 'total' structure length */ + KEY_LEN(sa) =3D KEY_LEN_INET; + KEY_LEN(mask) =3D KEY_LEN_INET; + mask.sin_addr.s_addr =3D htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);= + addr =3D *((in_addr_t *)paddr); + sa.sin_addr.s_addr =3D addr & mask.sin_addr.s_addr; + rnh_ptr =3D &ch->tables[tbl]; + sa_ptr =3D (struct sockaddr *)&sa; + mask_ptr =3D (struct sockaddr *)&mask; +#ifdef INET6 + } else if (plen =3D=3D sizeof(struct in6_addr)) { + /* IPv6 case */ + if (mlen > 128) + return (EINVAL); + struct sockaddr_in6 sa6, mask6; + memset(&sa6, 0, sizeof(struct sockaddr_in6)); + memset(&mask6, 0, sizeof(struct sockaddr_in6)); + /* Set 'total' structure length */ + KEY_LEN(sa6) =3D KEY_LEN_INET6; + KEY_LEN(mask6) =3D KEY_LEN_INET6; + ipv6_writemask(&mask6.sin6_addr, mlen); + memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); + APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); + rnh_ptr =3D &ch->xtables[tbl]; + sa_ptr =3D (struct sockaddr *)&sa6; + mask_ptr =3D (struct sockaddr *)&mask6; +#endif + } else { + /* Unknown CIDR type */ + return (EINVAL); + } + break; + + case IPFW_TABLE_INTERFACE: + /* Check if string is terminated */ + c =3D ((char *)paddr)[IF_NAMESIZE - 1]; + ((char *)paddr)[IF_NAMESIZE - 1] =3D '\0'; + if (((mlen =3D strlen((char *)paddr)) =3D=3D IF_NAMESIZE - 1) && (c !=3D= '\0')) + return (EINVAL); + + struct xaddr_iface ifname, ifmask; + memset(&ifname, 0, sizeof(ifname)); + + /* Set 'total' structure length */ + KEY_LEN(ifname) =3D mlen; + KEY_LEN(ifmask) =3D mlen; + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ +#if 0 + memset(ifmask.ifname, 0xFF, IF_NAMESIZE); + mask_ptr =3D (struct sockaddr *)&ifmask; +#endif + mask_ptr =3D NULL; + memcpy(ifname.ifname, paddr, mlen); + /* Set pointers */ + rnh_ptr =3D &ch->xtables[tbl]; + sa_ptr =3D (struct sockaddr *)&ifname; + + break; + + default: + return (EINVAL); + } + IPFW_WLOCK(ch); - ent =3D (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); - if (ent =3D=3D NULL) { + if ((rnh =3D *rnh_ptr) =3D=3D NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } + + if (ch->tabletype[tbl] !=3D type) { + IPFW_WUNLOCK(ch); + return (EINVAL); + } + + ent =3D (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); IPFW_WUNLOCK(ch); + + if (ent =3D=3D NULL) + return (ESRCH); + free(ent, M_IPFW_TBL); return (0); } @@ -158,15 +388,38 @@ flush_table_entry(struct radix_node *rn, void *arg int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) { - struct radix_node_head *rnh; + struct radix_node_head *rnh, *xrnh; =20 - IPFW_WLOCK_ASSERT(ch); - if (tbl >=3D IPFW_TABLES_MAX) return (EINVAL); - rnh =3D ch->tables[tbl]; - KASSERT(rnh !=3D NULL, ("NULL IPFW table")); - rnh->rnh_walktree(rnh, flush_table_entry, rnh); + + /* + * We free both (IPv4 and extended) radix trees and + * clear table type here to permit table to be reused + * for different type without module reload + */ + + IPFW_WLOCK(ch); + /* Set IPv4 table pointer to zero */ + if ((rnh =3D ch->tables[tbl]) !=3D NULL) + ch->tables[tbl] =3D NULL; + /* Set extended table pointer to zero */ + if ((xrnh =3D ch->xtables[tbl]) !=3D NULL) + ch->xtables[tbl] =3D NULL; + /* Zero table type */ + ch->tabletype[tbl] =3D 0; + IPFW_WUNLOCK(ch); + + if (rnh !=3D NULL) { + rnh->rnh_walktree(rnh, flush_table_entry, rnh); + rn_detachhead((void **)&rnh); + } + + if (xrnh !=3D NULL) { + xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); + rn_detachhead((void **)&xrnh); + } + return (0); } =20 @@ -174,31 +427,15 @@ void ipfw_destroy_tables(struct ip_fw_chain *ch) { uint16_t tbl; - struct radix_node_head *rnh; =20 - IPFW_WLOCK_ASSERT(ch); - - for (tbl =3D 0; tbl < IPFW_TABLES_MAX; tbl++) { + for (tbl =3D 0; tbl < IPFW_TABLES_MAX; tbl++) ipfw_flush_table(ch, tbl); - rnh =3D ch->tables[tbl]; - rn_detachhead((void **)&rnh); - } } =20 int ipfw_init_tables(struct ip_fw_chain *ch) -{=20 - int i; - uint16_t j; - - for (i =3D 0; i < IPFW_TABLES_MAX; i++) { - if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { - for (j =3D 0; j < i; j++) { - (void) ipfw_flush_table(ch, j); - } - return (ENOMEM); - } - } +{ + /* Init tables on demand */ return (0); } =20 @@ -212,8 +449,9 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t =20 if (tbl >=3D IPFW_TABLES_MAX) return (0); - rnh =3D ch->tables[tbl]; - KEY_LEN(sa) =3D 8; + if ((rnh =3D ch->tables[tbl]) =3D=3D NULL) + return (0); + KEY_LEN(sa) =3D KEY_LEN_INET; sa.sin_addr.s_addr =3D addr; ent =3D (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); if (ent !=3D NULL) { @@ -223,6 +461,45 @@ ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t return (0); } =20 +int +ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *p= addr, + uint32_t *val, int type) +{ + struct radix_node_head *rnh; + struct table_xentry *xent; + struct sockaddr_in6 sa6; + struct xaddr_iface iface; + + if (tbl >=3D IPFW_TABLES_MAX) + return (0); + if ((rnh =3D ch->xtables[tbl]) =3D=3D NULL) + return (0); + + switch (type) { + case IPFW_TABLE_CIDR: + KEY_LEN(sa6) =3D KEY_LEN_INET6; + memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); + xent =3D (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); + break; + + case IPFW_TABLE_INTERFACE: + KEY_LEN(iface) =3D strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE); + /* Assume direct match */ + /* FIXME: Add interface pattern matching */ + xent =3D (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); + break; + + default: + return (0); + } + + if (xent !=3D NULL) { + *val =3D xent->value; + return (1); + } + return (0); +} + static int count_table_entry(struct radix_node *rn, void *arg) { @@ -239,8 +516,9 @@ ipfw_count_table(struct ip_fw_chain *ch, uint32_t =20 if (tbl >=3D IPFW_TABLES_MAX) return (EINVAL); - rnh =3D ch->tables[tbl]; *cnt =3D 0; + if ((rnh =3D ch->tables[tbl]) =3D=3D NULL) + return (0); rnh->rnh_walktree(rnh, count_table_entry, cnt); return (0); } @@ -273,9 +551,122 @@ ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table =20 if (tbl->tbl >=3D IPFW_TABLES_MAX) return (EINVAL); - rnh =3D ch->tables[tbl->tbl]; tbl->cnt =3D 0; + if ((rnh =3D ch->tables[tbl->tbl]) =3D=3D NULL) + return (0); rnh->rnh_walktree(rnh, dump_table_entry, tbl); return (0); } + +static int +count_table_xentry(struct radix_node *rn, void *arg) +{ + uint32_t * const cnt =3D arg; + + (*cnt) +=3D sizeof(ipfw_table_xentry); + return (0); +} + +int +ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) +{ + struct radix_node_head *rnh; + + if (tbl >=3D IPFW_TABLES_MAX) + return (EINVAL); + *cnt =3D 0; + if ((rnh =3D ch->tables[tbl]) !=3D NULL) + rnh->rnh_walktree(rnh, count_table_xentry, cnt); + if ((rnh =3D ch->xtables[tbl]) !=3D NULL) + rnh->rnh_walktree(rnh, count_table_xentry, cnt); + /* Return zero if table is empty */ + if (*cnt > 0) + (*cnt) +=3D sizeof(ipfw_xtable); + return (0); +} + + +static int +dump_table_xentry_base(struct radix_node *rn, void *arg) +{ + struct table_entry * const n =3D (struct table_entry *)rn; + ipfw_xtable * const tbl =3D arg; + ipfw_table_xentry *xent; + + /* Out of memory, returning */ + if (tbl->cnt =3D=3D tbl->size) + return (1); + xent =3D &tbl->xent[tbl->cnt]; + xent->len =3D sizeof(ipfw_table_xentry); + xent->tbl =3D tbl->tbl; + if (in_nullhost(n->mask.sin_addr)) + xent->masklen =3D 0; + else + xent->masklen =3D 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); + /* Save IPv4 address as deprecated IPv6 compatible */ + xent->k.addr6.s6_addr32[3] =3D n->addr.sin_addr.s_addr; + xent->value =3D n->value; + tbl->cnt++; + return (0); +} + +static int +dump_table_xentry_extended(struct radix_node *rn, void *arg) +{ + struct table_xentry * const n =3D (struct table_xentry *)rn; + ipfw_xtable * const tbl =3D arg; + ipfw_table_xentry *xent; +#ifdef INET6 + int i; + uint32_t *v; +#endif + /* Out of memory, returning */ + if (tbl->cnt =3D=3D tbl->size) + return (1); + xent =3D &tbl->xent[tbl->cnt]; + xent->len =3D sizeof(ipfw_table_xentry); + xent->tbl =3D tbl->tbl; + + switch (tbl->type) { +#ifdef INET6 + case IPFW_TABLE_CIDR: + /* Count IPv6 mask */ + v =3D (uint32_t *)&n->m.mask6.sin6_addr; + for (i =3D 0; i < sizeof(struct in6_addr) / 4; i++, v++) + xent->masklen +=3D bitcount32(*v); + memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); + break; +#endif + case IPFW_TABLE_INTERFACE: + /* Assume exact mask */ + xent->masklen =3D 8 * IF_NAMESIZE; + memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); + break; +=09 + default: + /* unknown, skip entry */ + return (0); + } + + xent->value =3D n->value; + tbl->cnt++; + return (0); +} + +int +ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) +{ + struct radix_node_head *rnh; + + if (tbl->tbl >=3D IPFW_TABLES_MAX) + return (EINVAL); + tbl->cnt =3D 0; + tbl->type =3D ch->tabletype[tbl->tbl]; + if ((rnh =3D ch->tables[tbl->tbl]) !=3D NULL) + rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); + if ((rnh =3D ch->xtables[tbl->tbl]) !=3D NULL) + rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); + return (0); +} + /* end of file */ Index: sys/netinet/ipfw/ip_fw2.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- sys/netinet/ipfw/ip_fw2.c (revision 228874) +++ sys/netinet/ipfw/ip_fw2.c (working copy) @@ -1449,6 +1449,17 @@ do { \ ((ipfw_insn_u32 *)cmd)->d[0] =3D=3D v; else tablearg =3D v; + } else if (is_ipv6) { + uint32_t v =3D 0; + void *pkey =3D (cmd->opcode =3D=3D O_IP_DST_LOOKUP) ? + &args->f_id.dst_ip6: &args->f_id.src_ip6; + match =3D ipfw_lookup_table_extended(chain, + cmd->arg1, pkey, &v, + IPFW_TABLE_CIDR); + if (cmdlen =3D=3D F_INSN_SIZE(ipfw_insn_u32)) + match =3D ((ipfw_insn_u32 *)cmd)->d[0] =3D=3D v; + if (match) + tablearg =3D v; } break; =20 @@ -2630,12 +2641,12 @@ vnet_ipfw_uninit(const void *unused) IPFW_UH_WLOCK(chain); =20 IPFW_WLOCK(chain); + ipfw_dyn_uninit(0); /* run the callout_drain */ IPFW_WUNLOCK(chain); - IPFW_WLOCK(chain); =20 - ipfw_dyn_uninit(0); /* run the callout_drain */ ipfw_destroy_tables(chain); reap =3D NULL; + IPFW_WLOCK(chain); for (i =3D 0; i < chain->n_rules; i++) { rule =3D chain->map[i]; rule->x_next =3D reap; --------------030403040904080105030905-- --------------enigABFE081192EEA4308BF10E2C Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (FreeBSD) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk73OlgACgkQwcJ4iSZ1q2nzvQCfTDW9r7rVhSOHKD3QAEL93Myb x1EAn1z3xVlHujXf7Me5dvyiqOh4HQLB =hhVM -----END PGP SIGNATURE----- --------------enigABFE081192EEA4308BF10E2C--