From owner-svn-src-projects@FreeBSD.ORG Thu Jul 3 22:26:00 2014 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 4579B825; Thu, 3 Jul 2014 22:26:00 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 314FB2B96; Thu, 3 Jul 2014 22:26:00 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s63MQ0EG021912; Thu, 3 Jul 2014 22:26:00 GMT (envelope-from melifaro@svn.freebsd.org) Received: (from melifaro@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s63MPxJ9021900; Thu, 3 Jul 2014 22:25:59 GMT (envelope-from melifaro@svn.freebsd.org) Message-Id: <201407032225.s63MPxJ9021900@svn.freebsd.org> From: "Alexander V. Chernikov" Date: Thu, 3 Jul 2014 22:25:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r268239 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 03 Jul 2014 22:26:00 -0000 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<set_mask & (1<set_mask != (uint32_t)-1) ? " enable" : "enable"; for (i = 0; i < RESVD_SET; i++) - if (!(set_disable & (1<set_mask & (1<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 ***