Date: Thu, 3 Jul 2014 22:25:59 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r268239 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw Message-ID: <201407032225.s63MPxJ9021900@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Thu Jul 3 22:25:59 2014 New Revision: 268239 URL: http://svnweb.freebsd.org/changeset/base/268239 Log: Fully switch to named tables: Kernel changes: * Introduce ipfw_obj_tentry table entry structure to force u64 alignment. * Support "update-on-existing-key" "add" bahavior (TEI_FLAGS_UPDATED). * Use "subtype" field to distingush between IPv4 and IPv6 table records instead of previous hack. * Add value type (vtype) field for kernel tables. Current types are number,ip and dscp * Fix sets mask retrieval for old binaries * Fix crash while using interface tables Userland changes: * Switch ipfw_table_handler() to use named-only tables. * Add "table NAME create [type {cidr|iface|u32} [valtype {number|ip|dscp}] ..." * Switch ipfw_table_handler to match_token()-based parser. * Switch ipfw_sets_handler to use new ipfw_get_config() for mask retrieval. * Allow ipfw set X table ... syntax to permit using per-set table namespaces. Modified: projects/ipfw/sbin/ipfw/ipfw2.c projects/ipfw/sbin/ipfw/ipfw2.h projects/ipfw/sbin/ipfw/main.c projects/ipfw/sbin/ipfw/tables.c projects/ipfw/sys/netinet/ip_fw.h projects/ipfw/sys/netpfil/ipfw/ip_fw2.c projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Modified: projects/ipfw/sbin/ipfw/ipfw2.c ============================================================================== --- projects/ipfw/sbin/ipfw/ipfw2.c Thu Jul 3 21:48:19 2014 (r268238) +++ projects/ipfw/sbin/ipfw/ipfw2.c Thu Jul 3 22:25:59 2014 (r268239) @@ -174,7 +174,7 @@ static struct _s_x f_iptos[] = { { NULL, 0 } }; -static struct _s_x f_ipdscp[] = { +struct _s_x f_ipdscp[] = { { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */ { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */ { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */ @@ -649,7 +649,7 @@ match_token(struct _s_x *table, char *st for (pt = table ; i && pt->s != NULL ; pt++) if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) return pt->x; - return -1; + return (-1); } /** @@ -665,6 +665,25 @@ match_value(struct _s_x *p, int value) return NULL; } +size_t +concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter) +{ + struct _s_x *pt; + int l; + size_t sz; + + for (sz = 0, pt = table ; pt->s != NULL; pt++) { + l = snprintf(buf + sz, bufsize - sz, "%s%s", + (sz == 0) ? "" : delimiter, pt->s); + sz += l; + bufsize += l; + if (sz > bufsize) + return (bufsize); + } + + return (sz); +} + /* * _substrcmp takes two strings and returns 1 if they do not match, * and 0 if they match exactly or the first string is a sub-string @@ -2012,46 +2031,38 @@ show_dyn_state(struct cmdline_opts *co, void ipfw_sets_handler(char *av[]) { - uint32_t set_disable, masks[2]; - int i, nbytes; + uint32_t masks[2]; + int i; uint16_t rulenum; uint8_t cmd, new_set; + char *msg; + size_t size; av++; if (av[0] == NULL) errx(EX_USAGE, "set needs command"); if (_substrcmp(*av, "show") == 0) { - void *data = NULL; - char const *msg; - int nalloc; - - nalloc = nbytes = sizeof(struct ip_fw); - while (nbytes >= nalloc) { - if (data) - free(data); - nalloc = nalloc * 2 + 200; - nbytes = nalloc; - data = safe_calloc(1, nbytes); - if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0) - err(EX_OSERR, "getsockopt(IP_FW_GET)"); - } + struct format_opts fo; + ipfw_cfg_lheader *cfg; - bcopy(&((struct ip_fw *)data)->next_rule, - &set_disable, sizeof(set_disable)); + memset(&fo, 0, sizeof(fo)); + if (ipfw_get_config(&co, &fo, &cfg, &size) != 0) + err(EX_OSERR, "requesting config failed"); - for (i = 0, msg = "disable" ; i < RESVD_SET; i++) - if ((set_disable & (1<<i))) { + for (i = 0, msg = "disable"; i < RESVD_SET; i++) + if ((cfg->set_mask & (1<<i)) == 0) { printf("%s %d", msg, i); msg = ""; } - msg = (set_disable) ? " enable" : "enable"; + msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable"; for (i = 0; i < RESVD_SET; i++) - if (!(set_disable & (1<<i))) { + if ((cfg->set_mask & (1<<i)) != 0) { printf("%s %d", msg, i); msg = ""; } printf("\n"); + free(cfg); } else if (_substrcmp(*av, "swap") == 0) { av++; if ( av[0] == NULL || av[1] == NULL ) Modified: projects/ipfw/sbin/ipfw/ipfw2.h ============================================================================== --- projects/ipfw/sbin/ipfw/ipfw2.h Thu Jul 3 21:48:19 2014 (r268238) +++ projects/ipfw/sbin/ipfw/ipfw2.h Thu Jul 3 22:25:59 2014 (r268239) @@ -71,6 +71,8 @@ struct _s_x { int x; }; +extern struct _s_x f_ipdscp[]; + enum tokens { TOK_NULL=0, @@ -205,6 +207,16 @@ enum tokens { TOK_LOOKUP, TOK_SOCKARG, TOK_SETDSCP, + /* Table tokens */ + TOK_CREATE, + TOK_DESTROY, + TOK_LIST, + TOK_INFO, + TOK_FLUSH, + TOK_ADD, + TOK_DEL, + TOK_VALTYPE, + TOK_ALGO, }; /* * the following macro returns an error message if we run out of @@ -238,6 +250,8 @@ int _substrcmp2(const char *str1, const /* utility functions */ int match_token(struct _s_x *table, char *string); char const *match_value(struct _s_x *p, int value); +size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table, + char *delimiter); struct _ip_fw3_opheader; int do_cmd(int optname, void *optval, uintptr_t optlen); Modified: projects/ipfw/sbin/ipfw/main.c ============================================================================== --- projects/ipfw/sbin/ipfw/main.c Thu Jul 3 21:48:19 2014 (r268238) +++ projects/ipfw/sbin/ipfw/main.c Thu Jul 3 22:25:59 2014 (r268239) @@ -436,6 +436,8 @@ ipfw_main(int oldac, char **oldav) ipfw_list(ac, av, do_acct); else if (_substrcmp(*av, "show") == 0) ipfw_list(ac, av, 1 /* show counters */); + else if (_substrcmp(*av, "table") == 0) + ipfw_table_handler(ac, av); else errx(EX_USAGE, "bad command `%s'", *av); } Modified: projects/ipfw/sbin/ipfw/tables.c ============================================================================== --- projects/ipfw/sbin/ipfw/tables.c Thu Jul 3 21:48:19 2014 (r268238) +++ projects/ipfw/sbin/ipfw/tables.c Thu Jul 3 22:25:59 2014 (r268239) @@ -49,18 +49,27 @@ #include "ipfw2.h" static void table_list(ipfw_xtable_info *i, int need_header); -static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); -static int table_flush(char *name, uint32_t set); -static int table_destroy(char *name, uint32_t set); -static int table_get_info(char *name, uint32_t set, ipfw_xtable_info *i); +static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[], + int add, int update); +static int table_flush(ipfw_obj_header *oh); +static int table_destroy(ipfw_obj_header *oh); +static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i); +static void table_create(ipfw_obj_header *oh, int ac, char *av[]); +static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i); static int table_show_info(ipfw_xtable_info *i, void *arg); -static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx); +static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, + uint16_t uidx); static int table_flush_one(ipfw_xtable_info *i, void *arg); static int table_show_one(ipfw_xtable_info *i, void *arg); static int table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh); static void table_show_list(ipfw_obj_header *oh, int need_header); +static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, + char *key, uint8_t *ptype, uint8_t *pvtype); +static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, + char *arg, uint8_t type, uint8_t vtype); + typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg); static int tables_foreach(table_cb_t *f, void *arg, int sort); @@ -68,6 +77,31 @@ static int tables_foreach(table_cb_t *f, #define s6_addr32 __u6_addr.__u6_addr32 #endif +static struct _s_x tabletypes[] = { + { "cidr", IPFW_TABLE_CIDR }, + { "iface", IPFW_TABLE_INTERFACE }, + { "u32", IPFW_TABLE_U32 }, + { NULL, 0 } +}; + +static struct _s_x tablevaltypes[] = { + { "dscp", IPFW_VTYPE_DSCP }, + { "ip", IPFW_VTYPE_IP }, + { "number", IPFW_VTYPE_U32 }, + { NULL, 0 } +}; + +static struct _s_x tablecmds[] = { + { "add", TOK_ADD }, + { "create", TOK_CREATE }, + { "delete", TOK_DEL }, + { "destroy", TOK_DESTROY }, + { "flush", TOK_FLUSH }, + { "info", TOK_INFO }, + { "list", TOK_LIST }, + { NULL, 0 } +}; + static int lookup_host (char *host, struct in_addr *ipaddr) { @@ -83,305 +117,280 @@ lookup_host (char *host, struct in_addr /* * This one handles all table-related commands - * ipfw table N add addr[/masklen] [value] - * ipfw table N delete addr[/masklen] - * ipfw table {N | all} flush - * ipfw table {N | all} list - * ipfw table {N | all} info + * ipfw table NAME create ... + * ipfw table NAME destroy + * ipfw table NAME add addr[/masklen] [value] + * ipfw table NAME delete addr[/masklen] + * ipfw table {NAME | all} flush + * ipfw table {NAME | all} list + * ipfw table {NAME | all} info */ void ipfw_table_handler(int ac, char *av[]) { - ipfw_table_xentry *xent; - int do_add; - int is_all; - uint32_t set; - int error; - char xbuf[sizeof(ip_fw3_opheader) + sizeof(ipfw_table_xentry)]; - ip_fw3_opheader *op3; + int do_add, is_all; + int error, tcmd; + ipfw_xtable_info i; + ipfw_obj_header oh; char *tablename; + uint32_t set; - memset(xbuf, 0, sizeof(xbuf)); - op3 = (ip_fw3_opheader *)xbuf; - xent = (ipfw_table_xentry *)(op3 + 1); + memset(&oh, 0, sizeof(oh)); + is_all = 0; + if (co.use_set != 0) + set = co.use_set - 1; + else + set = 0; ac--; av++; tablename = *av; - set = 0; - if (ac && isdigit(**av)) { - xent->tbl = atoi(*av); - is_all = 0; - ac--; av++; - } else if (ac && _substrcmp(*av, "all") == 0) { - xent->tbl = 0; - is_all = 1; - ac--; av++; - } else - errx(EX_USAGE, "table number or 'all' keyword required"); + + if (table_check_name(tablename) == 0) { + table_fill_ntlv(&oh.ntlv, *av, set, 1); + //oh->set = set; + oh.idx = 1; + } else { + if (strcmp(tablename, "all") == 0) + is_all = 1; + else + errx(EX_USAGE, "table name %s is invalid", tablename); + } + ac--; av++; + + if ((tcmd = match_token(tablecmds, *av)) == -1) + errx(EX_USAGE, "invalid table command %s", *av); + NEED1("table needs command"); - if (is_all && _substrcmp(*av, "list") != 0 - && _substrcmp(*av, "info") != 0 - && _substrcmp(*av, "flush") != 0) - errx(EX_USAGE, "table number required"); + switch (tcmd) { + case TOK_LIST: + case TOK_INFO: + case TOK_FLUSH: + break; + default: + if (is_all != 0) + errx(EX_USAGE, "table name required"); + } - if (_substrcmp(*av, "add") == 0 || - _substrcmp(*av, "delete") == 0) { + switch (tcmd) { + case TOK_ADD: + case TOK_DEL: do_add = **av == 'a'; ac--; av++; - if (!ac) - errx(EX_USAGE, "address required"); - - table_fill_xentry(*av, xent); - + table_modify_record(&oh, ac, av, do_add, co.do_quiet); + break; + case TOK_CREATE: ac--; av++; - if (do_add && ac) { - unsigned int tval; - /* isdigit is a bit of a hack here.. */ - if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { - xent->value = strtoul(*av, NULL, 0); - } else { - if (lookup_host(*av, (struct in_addr *)&tval) == 0) { - /* The value must be stored in host order * - * so that the values < 65k can be distinguished */ - xent->value = ntohl(tval); - } else { - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); - } - } - } else - xent->value = 0; - if (do_set3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL, - op3, sizeof(xbuf)) < 0) { - /* If running silent, don't bomb out on these errors. */ - if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH)))) - err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)", - do_add ? "XADD" : "XDEL"); - /* In silent mode, react to a failed add by deleting */ - if (do_add) { - do_set3(IP_FW_TABLE_XDEL, op3, sizeof(xbuf)); - if (do_set3(IP_FW_TABLE_XADD, op3, sizeof(xbuf)) < 0) - err(EX_OSERR, - "setsockopt(IP_FW_TABLE_XADD)"); - } - } - } else if (_substrcmp(*av, "flush") == 0) { + table_create(&oh, ac, av); + break; + case TOK_DESTROY: + if (table_destroy(&oh) != 0) + err(EX_OSERR, "failed to destroy table %s", tablename); + break; + case TOK_FLUSH: if (is_all == 0) { - if ((error = table_flush(tablename, set)) != 0) + if ((error = table_flush(&oh)) != 0) err(EX_OSERR, "failed to flush table %s info", tablename); } else { - error = tables_foreach(table_flush_one, NULL, 1); + error = tables_foreach(table_flush_one, &oh, 1); if (error != 0) err(EX_OSERR, "failed to flush tables list"); } - } else if (_substrcmp(*av, "list") == 0) { + break; + case TOK_INFO: if (is_all == 0) { - ipfw_xtable_info i; - if ((error = table_get_info(tablename, set, &i)) != 0) + if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); - table_show_one(&i, NULL); + table_show_info(&i, NULL); } else { - error = tables_foreach(table_show_one, NULL, 1); + error = tables_foreach(table_show_info, NULL, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } - } else if (_substrcmp(*av, "destroy") == 0) { - if (table_destroy(tablename, set) != 0) - err(EX_OSERR, "failed to destroy table %s", tablename); - } else if (_substrcmp(*av, "info") == 0) { + break; + case TOK_LIST: if (is_all == 0) { ipfw_xtable_info i; - if ((error = table_get_info(tablename, set, &i)) != 0) + if ((error = table_get_info(&oh, &i)) != 0) err(EX_OSERR, "failed to request table info"); - table_show_info(&i, NULL); + table_show_one(&i, NULL); } else { - error = tables_foreach(table_show_info, NULL, 1); + error = tables_foreach(table_show_one, NULL, 1); if (error != 0) err(EX_OSERR, "failed to request tables list"); } - } else - errx(EX_USAGE, "invalid table command %s", *av); + break; + } } static void -table_fill_xentry(char *arg, ipfw_table_xentry *xent) +table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx) { - int addrlen, mask, masklen, type; - struct in6_addr *paddr; - uint32_t *pkey; - char *p; - uint32_t key; - mask = 0; - type = 0; - addrlen = 0; - masklen = 0; + ntlv->head.type = IPFW_TLV_TBL_NAME; + ntlv->head.length = sizeof(ipfw_obj_ntlv); + ntlv->idx = uidx; + ntlv->set = set; + strlcpy(ntlv->name, name, sizeof(ntlv->name)); +} - /* - * Let's try to guess type by agrument. - * Possible types: - * 1) IPv4[/mask] - * 2) IPv6[/mask] - * 3) interface name - * 4) port, uid/gid or other u32 key (base 10 format) - * 5) hostname - */ - paddr = &xent->k.addr6; - if (ishexnumber(*arg) != 0 || *arg == ':') { - /* Remove / if exists */ - if ((p = strchr(arg, '/')) != NULL) { - *p = '\0'; - mask = atoi(p + 1); - } +static void +table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) +{ - if (inet_pton(AF_INET, arg, paddr) == 1) { - if (p != NULL && mask > 32) - errx(EX_DATAERR, "bad IPv4 mask width: %s", - p + 1); + oh->set = i->set; + oh->idx = 1; + table_fill_ntlv(&oh->ntlv, i->tablename, oh->set, 1); +} - type = IPFW_TABLE_CIDR; - masklen = p ? mask : 32; - addrlen = sizeof(struct in_addr); - } else if (inet_pton(AF_INET6, arg, paddr) == 1) { - if (IN6_IS_ADDR_V4COMPAT(paddr)) - errx(EX_DATAERR, - "Use IPv4 instead of v4-compatible"); - if (p != NULL && mask > 128) - errx(EX_DATAERR, "bad IPv6 mask width: %s", - p + 1); +static struct _s_x tablenewcmds[] = { + { "type", TOK_TYPE}, + { "valtype", TOK_VALTYPE }, + { "algo", TOK_ALGO }, + { NULL, 0 } +}; - type = IPFW_TABLE_CIDR; - masklen = p ? mask : 128; - addrlen = sizeof(struct in6_addr); - } else { - /* Port or any other key */ - /* Skip non-base 10 entries like 'fa1' */ - key = strtol(arg, &p, 10); - if (*p == '\0') { - pkey = (uint32_t *)paddr; - *pkey = htonl(key); - type = IPFW_TABLE_CIDR; - masklen = 32; - addrlen = sizeof(uint32_t); - } else if ((p != arg) && (*p == '.')) { - /* - * Warn on IPv4 address strings - * which are "valid" for inet_aton() but not - * in inet_pton(). - * - * Typical examples: '10.5' or '10.0.0.05' - */ - errx(EX_DATAERR, - "Invalid IPv4 address: %s", arg); - } - } - } +/* + * Creates new table + * + * ipfw table NAME create [ type { cidr | iface | u32 } ] + * [ valtype { number | ip | dscp } ] + * [ algo algoname ] + * + * Request: [ ipfw_obj_header ipfw_xtable_info ] + */ +static void +table_create(ipfw_obj_header *oh, int ac, char *av[]) +{ + ipfw_xtable_info xi; + int error, tcmd, val; + size_t sz; + char tbuf[128]; - if (type == 0 && strchr(arg, '.') == NULL) { - /* Assume interface name. Copy significant data only */ - mask = MIN(strlen(arg), IF_NAMESIZE - 1); - memcpy(xent->k.iface, arg, mask); - /* Set mask to exact match */ - masklen = 8 * IF_NAMESIZE; - type = IPFW_TABLE_INTERFACE; - addrlen = IF_NAMESIZE; - } + sz = sizeof(tbuf); + memset(&xi, 0, sizeof(xi)); - if (type == 0) { - if (lookup_host(arg, (struct in_addr *)paddr) != 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", arg); + /* Set some defaults to preserve compability */ + xi.type = IPFW_TABLE_CIDR; + xi.vtype = IPFW_VTYPE_U32; + + while (ac > 0) { + if ((tcmd = match_token(tablenewcmds, *av)) == -1) + errx(EX_USAGE, "unknown option: %s", *av); + ac--; av++; - masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); + switch (tcmd) { + case TOK_TYPE: + NEED1("table type required"); + val = match_token(tabletypes, *av); + if (val != -1) { + printf("av %s type %d\n", *av, xi.type); + xi.type = val; + ac--; av++; + break; + } + concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", "); + errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s", + *av, tbuf); + break; + case TOK_VALTYPE: + NEED1("table value type required"); + val = match_token(tablevaltypes, *av); + if (val != -1) { + xi.vtype = val; + ac--; av++; + break; + } + concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", "); + errx(EX_USAGE, "Unknown value type: %s. Supported: %s", + *av, tbuf); + break; + case TOK_ALGO: + NEED1("table algorithm name required"); + if (strlen(*av) > sizeof(xi.algoname)) + errx(EX_USAGE, "algorithm name too long"); + strlcpy(xi.algoname, *av, sizeof(xi.algoname)); + ac--; av++; + break; + } } - xent->type = type; - xent->masklen = masklen; - xent->len = offsetof(ipfw_table_xentry, k) + addrlen; + if ((error = table_do_create(oh, &xi)) != 0) + err(EX_OSERR, "Table creation failed"); } -static void -table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx) +/* + * Creates new table + * + * Request: [ ipfw_obj_header ipfw_xtable_info ] + * + * Returns 0 on success. + */ +static int +table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i) { + char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; + int error; - ntlv->head.type = IPFW_TLV_TBL_NAME; - ntlv->head.length = sizeof(ipfw_obj_ntlv); - ntlv->idx = uidx; - strlcpy(ntlv->name, name, sizeof(ntlv->name)); -} + memcpy(tbuf, oh, sizeof(*oh)); + memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); + oh = (ipfw_obj_header *)tbuf; -static void -table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) -{ + error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf)); - oh->set = i->set; - oh->idx = 1; - table_fill_ntlv(&oh->ntlv, i->tablename, 1); + return (error); } /* - * Destroys given table @name in given @set. + * Destroys given table specified by @oh->ntlv. * Returns 0 on success. */ static int -table_destroy(char *name, uint32_t set) +table_destroy(ipfw_obj_header *oh) { - ipfw_obj_header oh; - memset(&oh, 0, sizeof(oh)); - oh.idx = 1; - table_fill_ntlv(&oh.ntlv, name, 1); - if (do_set3(IP_FW_TABLE_XDESTROY, &oh.opheader, sizeof(oh)) != 0) + if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } /* - * Flushes given table @name in given @set. + * Flushes given table specified by @oh->ntlv. * Returns 0 on success. */ static int -table_flush(char *name, uint32_t set) +table_flush(ipfw_obj_header *oh) { - ipfw_obj_header oh; - memset(&oh, 0, sizeof(oh)); - oh.idx = 1; - table_fill_ntlv(&oh.ntlv, name, 1); - if (do_set3(IP_FW_TABLE_XFLUSH, &oh.opheader, sizeof(oh)) != 0) + if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0) return (-1); return (0); } /* - * Retrieves info for given table @name in given @set and stores + * Retrieves table in given table specified by @oh->ntlv. * it inside @i. * Returns 0 on success. */ static int -table_get_info(char *name, uint32_t set, ipfw_xtable_info *i) +table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i) { - char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)]; - ipfw_obj_header *oh; + char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; + int error; size_t sz; sz = sizeof(tbuf); memset(tbuf, 0, sizeof(tbuf)); + memcpy(tbuf, oh, sizeof(*oh)); oh = (ipfw_obj_header *)tbuf; - i->set = set; - strlcpy(i->tablename, name, sizeof(i->tablename)); - - table_fill_objheader(oh, i); - - if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) < 0) - return (-1); + if ((error = do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz)) != 0) + return (error); if (sz < sizeof(tbuf)) - return (-1); + return (EINVAL); *i = *(ipfw_xtable_info *)(oh + 1); @@ -394,21 +403,16 @@ table_get_info(char *name, uint32_t set, static int table_show_info(ipfw_xtable_info *i, void *arg) { - char *type; + const char *ttype, *vtype; printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); - switch (i->type) { - case IPFW_TABLE_CIDR: - type = "cidr"; - break; - case IPFW_TABLE_INTERFACE: - type = "iface"; - break; - default: - type = "unknown"; - } - printf(" type: %s, kindex: %d\n", type, i->kidx); - printf(" ftype: %d, algorithm: %d\n", i->ftype, i->atype); + if ((ttype = match_value(tabletypes, i->type)) == NULL) + ttype = "unknown"; + if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL) + vtype = "unknown"; + + printf(" type: %s, kindex: %d\n", ttype, i->kidx); + printf(" valtype: %s, algorithm: %s\n", vtype, i->algoname); printf(" references: %u\n", i->refcnt); printf(" items: %u, size: %u\n", i->count, i->size); @@ -426,7 +430,7 @@ table_show_one(ipfw_xtable_info *i, void { ipfw_obj_header *oh; - if ((oh = malloc(i->size)) == NULL) + if ((oh = calloc(1, i->size)) == NULL) return (ENOMEM); if (table_get_list(i, oh) == 0) @@ -439,11 +443,244 @@ table_show_one(ipfw_xtable_info *i, void static int table_flush_one(ipfw_xtable_info *i, void *arg) { + ipfw_obj_header *oh; + + oh = (ipfw_obj_header *)arg; + + table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1); + + return (table_flush(oh)); +} + +static int +table_do_modify_record(int cmd, ipfw_obj_header *oh, + ipfw_obj_tentry *tent, int update) +{ + char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)]; + int error; + + memset(xbuf, 0, sizeof(xbuf)); + memcpy(xbuf, oh, sizeof(*oh)); + oh = (ipfw_obj_header *)xbuf; + oh->opheader.version = 1; + + memcpy(oh + 1, tent, sizeof(*tent)); + tent = (ipfw_obj_tentry *)(oh + 1); + if (update != 0) + tent->flags |= IPFW_TF_UPDATE; + tent->head.length = sizeof(ipfw_obj_tentry); + + error = do_set3(cmd, &oh->opheader, sizeof(xbuf)); + + return (error); +} + +static void +table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update) +{ + ipfw_obj_tentry tent; + uint8_t type, vtype; + int cmd; + char *texterr; + + if (ac == 0) + errx(EX_USAGE, "address required"); + + memset(&tent, 0, sizeof(tent)); + tent.head.length = sizeof(tent); + tent.idx = 1; + + tentry_fill_key(oh, &tent, *av, &type, &vtype); + oh->ntlv.type = type; + ac--; av++; + + if (add != 0) { + if (ac > 0) + tentry_fill_value(oh, &tent, *av, type, vtype); + cmd = IP_FW_TABLE_XADD; + texterr = "setsockopt(IP_FW_TABLE_XADD)"; + } else { + cmd = IP_FW_TABLE_XDEL; + texterr = "setsockopt(IP_FW_TABLE_XDEL)"; + } - return (table_flush(i->tablename, i->set)); + if (table_do_modify_record(cmd, oh, &tent, update) != 0) + err(EX_OSERR, "%s", texterr); } +static void +tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type) +{ + char *p; + int mask, af; + struct in6_addr *paddr; + uint32_t key, *pkey; + int masklen; + + masklen = 0; + af = 0; + paddr = (struct in6_addr *)&tentry->k; + + switch (type) { + case IPFW_TABLE_CIDR: + /* Remove / if exists */ + if ((p = strchr(arg, '/')) != NULL) { + *p = '\0'; + mask = atoi(p + 1); + } + + if (inet_pton(AF_INET, arg, paddr) == 1) { + if (p != NULL && mask > 32) + errx(EX_DATAERR, "bad IPv4 mask width: %s", + p + 1); + + masklen = p ? mask : 32; + af = AF_INET; + } else if (inet_pton(AF_INET6, arg, paddr) == 1) { + if (IN6_IS_ADDR_V4COMPAT(paddr)) + errx(EX_DATAERR, + "Use IPv4 instead of v4-compatible"); + if (p != NULL && mask > 128) + errx(EX_DATAERR, "bad IPv6 mask width: %s", + p + 1); + + masklen = p ? mask : 128; + af = AF_INET6; + } else { + /* Assume FQDN */ + if (lookup_host(arg, (struct in_addr *)paddr) != 0) + errx(EX_NOHOST, "hostname ``%s'' unknown", arg); + + masklen = 32; + type = IPFW_TABLE_CIDR; + af = AF_INET; + } + break; + case IPFW_TABLE_INTERFACE: + /* Assume interface name. Copy significant data only */ + mask = MIN(strlen(arg), IF_NAMESIZE - 1); + memcpy(paddr, arg, mask); + /* Set mask to exact match */ + masklen = 8 * IF_NAMESIZE; + break; + case IPFW_TABLE_U32: + /* Port or any other key */ + key = strtol(arg, &p, 10); + if (*p != '\0') + errx(EX_DATAERR, "Invalid number: %s", arg); + + pkey = (uint32_t *)paddr; + *pkey = key; + masklen = 32; + break; + default: + errx(EX_DATAERR, "Unsupported table type: %d", type); + } + + tentry->subtype = af; + tentry->masklen = masklen; +} + +static void +tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key, + uint8_t *ptype, uint8_t *pvtype) +{ + ipfw_xtable_info xi; + uint8_t type, vtype; + int error; + + type = 0; + vtype = 0; + + /* + * Compability layer. Try to interpret data as CIDR first. + */ + if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 || + inet_pton(AF_INET6, key, &tent->k.addr6) == 1) { + /* OK Prepare and send */ + type = IPFW_TABLE_CIDR; + } else { + + /* + * Non-CIDR of FQDN hostname. Ask kernel + * about given table. + */ + error = table_get_info(oh, &xi); + if (error == ESRCH) + errx(EX_USAGE, "Table %s does not exist, cannot intuit " + "key type", oh->ntlv.name); + else if (error != 0) + errx(EX_OSERR, "Error requesting table %s info", + oh->ntlv.name); + + /* Table found. */ + type = xi.type; + vtype = xi.vtype; + } + + tentry_fill_key_type(key, tent, type); + + *ptype = type; + *pvtype = vtype; +} + +static void +tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg, + uint8_t type, uint8_t vtype) +{ + ipfw_xtable_info xi; + int error; + int code; + char *p; + + if (vtype == 0) { + /* Format type is unknown, ask kernel */ + error = table_get_info(oh, &xi); + if (error == ESRCH) { + + /* + * XXX: This one may break some scripts. + * Change this behavior for MFC. + */ + errx(EX_USAGE, "Table %s does not exist. Unable to " + "guess value format.", oh->ntlv.name); + } else if (error != 0) + errx(EX_OSERR, "Error requesting table %s info", + oh->ntlv.name); + + vtype = xi.vtype; + } + + switch (vtype) { + case IPFW_VTYPE_U32: + tent->value = strtoul(arg, &p, 0); + if (*p != '\0') + errx(EX_USAGE, "Invalid number: %s", arg); + break; + case IPFW_VTYPE_IP: + if (inet_pton(AF_INET, arg, &tent->value) == 1) + break; + /* Try hostname */ + if (lookup_host(arg, (struct in_addr *)&tent->value) != 0) + errx(EX_USAGE, "Invalid IPv4 address: %s", arg); + break; + case IPFW_VTYPE_DSCP: + if (isalpha(*arg)) { + if ((code = match_token(f_ipdscp, arg)) == -1) + errx(EX_DATAERR, "Unknown DSCP code"); + } else { + code = strtoul(arg, NULL, 10); + if (code < 0 || code > 63) + errx(EX_DATAERR, "Invalid DSCP value"); + } + tent->value = code; + break; + default: + errx(EX_OSERR, "Unsupported format type %d", vtype); + } +} + /* * Compare table names. * Honor number comparison. @@ -668,14 +905,11 @@ table_check_name(char *tablename) /* * Check if tablename is null-terminated and contains * valid symbols only. Valid mask is: - * [a-zA-Z\-\.][a-zA-Z0-9\-_\.]{0,62} *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201407032225.s63MPxJ9021900>