diff options
author | melifaro <melifaro@FreeBSD.org> | 2014-07-03 22:25:59 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2014-07-03 22:25:59 +0000 |
commit | 99023231d3e6ab3f80cecab0626d12352069bedf (patch) | |
tree | 698d237c2de20d6a6adb97f520a6f5a63853b737 /sbin/ipfw/tables.c | |
parent | 75913dd997a81341ee4e07a64ff5f6d7ccec1d2b (diff) | |
download | FreeBSD-src-99023231d3e6ab3f80cecab0626d12352069bedf.zip FreeBSD-src-99023231d3e6ab3f80cecab0626d12352069bedf.tar.gz |
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.
Diffstat (limited to 'sbin/ipfw/tables.c')
-rw-r--r-- | sbin/ipfw/tables.c | 700 |
1 files changed, 469 insertions, 231 deletions
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index e34b140..ad7ea99 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -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, void *arg, int sort); #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 *ipaddr) /* * 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; - masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); + while (ac > 0) { + if ((tcmd = match_token(tablenewcmds, *av)) == -1) + errx(EX_USAGE, "unknown option: %s", *av); + ac--; av++; + + 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, ipfw_xtable_info *i) 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 *arg) { 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,10 +443,243 @@ table_show_one(ipfw_xtable_info *i, void *arg) 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)"; + } + + 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); - return (table_flush(i->tablename, i->set)); + *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. @@ -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} + * [a-zA-Z0-9\-_\.]{1,63} */ l = strlen(tablename); if (l == 0 || l >= 64) return (EINVAL); - /* Restrict first symbol to non-digit */ - if (isdigit(tablename[0])) - return (EINVAL); for (i = 0; i < l; i++) { c = tablename[i]; if (isalpha(c) || isdigit(c) || c == '_' || @@ -684,6 +918,10 @@ table_check_name(char *tablename) return (EINVAL); } + /* Restrict some 'special' names */ + if (strcmp(tablename, "all") == 0) + return (EINVAL); + return (0); } |