From ac35ff17842a49c0fc98ad5afda2410078651a86 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" Date: Thu, 3 Jul 2014 22:25:59 +0000 Subject: [PATCH] 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. --- sbin/ipfw/ipfw2.c | 59 ++- sbin/ipfw/ipfw2.h | 14 + sbin/ipfw/main.c | 2 + sbin/ipfw/tables.c | 712 +++++++++++++++++++--------- sys/netinet/ip_fw.h | 43 +- sys/netpfil/ipfw/ip_fw2.c | 5 +- sys/netpfil/ipfw/ip_fw_private.h | 19 +- sys/netpfil/ipfw/ip_fw_sockopt.c | 141 ++---- sys/netpfil/ipfw/ip_fw_table.c | 428 +++++++++++++---- sys/netpfil/ipfw/ip_fw_table.h | 34 +- sys/netpfil/ipfw/ip_fw_table_algo.c | 80 +++- 11 files changed, 1028 insertions(+), 509 deletions(-) diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 065f6b129020..88c35e3847a5 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -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 *string) 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, struct format_opts *fo, 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; + struct format_opts fo; + ipfw_cfg_lheader *cfg; - 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)"); - } + memset(&fo, 0, sizeof(fo)); + if (ipfw_get_config(&co, &fo, &cfg, &size) != 0) + err(EX_OSERR, "requesting config failed"); - bcopy(&((struct ip_fw *)data)->next_rule, - &set_disable, sizeof(set_disable)); - - 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"); - 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"); - if (_substrcmp(*av, "add") == 0 || - _substrcmp(*av, "delete") == 0) { + 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"); + switch (tcmd) { + case TOK_LIST: + case TOK_INFO: + case TOK_FLUSH: + break; + default: + if (is_all != 0) + errx(EX_USAGE, "table name required"); + } + + 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) - err(EX_OSERR, "failed to request table info"); - table_show_one(&i, NULL); - } else { - error = tables_foreach(table_show_one, 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) { - 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); } else { @@ -199,115 +207,30 @@ ipfw_table_handler(int ac, char *av[]) if (error != 0) err(EX_OSERR, "failed to request tables list"); } - } else - errx(EX_USAGE, "invalid table command %s", *av); -} - -static void -table_fill_xentry(char *arg, ipfw_table_xentry *xent) -{ - 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; - - /* - * 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); - } - - if (inet_pton(AF_INET, arg, paddr) == 1) { - if (p != NULL && mask > 32) - errx(EX_DATAERR, "bad IPv4 mask width: %s", - p + 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); - - type = IPFW_TABLE_CIDR; - masklen = p ? mask : 128; - addrlen = sizeof(struct in6_addr); + break; + case TOK_LIST: + if (is_all == 0) { + ipfw_xtable_info i; + if ((error = table_get_info(&oh, &i)) != 0) + err(EX_OSERR, "failed to request table info"); + table_show_one(&i, NULL); } 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); - } + error = tables_foreach(table_show_one, NULL, 1); + if (error != 0) + err(EX_OSERR, "failed to request tables list"); } + break; } - - 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; - } - - if (type == 0) { - if (lookup_host(arg, (struct in_addr *)paddr) != 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", arg); - - masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); - } - - xent->type = type; - xent->masklen = masklen; - xent->len = offsetof(ipfw_table_xentry, k) + addrlen; } static void -table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx) +table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx) { 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)); } @@ -317,71 +240,157 @@ table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i) oh->set = i->set; oh->idx = 1; - table_fill_ntlv(&oh->ntlv, i->tablename, 1); + table_fill_ntlv(&oh->ntlv, i->tablename, oh->set, 1); +} + +static struct _s_x tablenewcmds[] = { + { "type", TOK_TYPE}, + { "valtype", TOK_VALTYPE }, + { "algo", TOK_ALGO }, + { NULL, 0 } +}; + +/* + * 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]; + + sz = sizeof(tbuf); + memset(&xi, 0, sizeof(xi)); + + /* 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++; + + 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; + } + } + + if ((error = table_do_create(oh, &xi)) != 0) + err(EX_OSERR, "Table creation failed"); } /* - * Destroys given table @name in given @set. + * Creates new table + * + * Request: [ ipfw_obj_header ipfw_xtable_info ] + * * Returns 0 on success. */ static int -table_destroy(char *name, uint32_t set) +table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i) { - ipfw_obj_header oh; + char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)]; + int error; - 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) + memcpy(tbuf, oh, sizeof(*oh)); + memcpy(tbuf + sizeof(*oh), i, sizeof(*i)); + oh = (ipfw_obj_header *)tbuf; + + error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf)); + + return (error); +} + +/* + * Destroys given table specified by @oh->ntlv. + * Returns 0 on success. + */ +static int +table_destroy(ipfw_obj_header *oh) +{ + + 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; - return (table_flush(i->tablename, i->set)); + 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); + + *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); } diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index 2f766f234d37..58079902fc1d 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -86,7 +86,7 @@ typedef struct _ip_fw3_opheader { #define IP_FW_TABLE_XINFO 93 /* request info for one table */ #define IP_FW_TABLE_XFLUSH 94 /* flush table data */ #define IP_FW_TABLE_XCREATE 95 /* create new table */ -#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */ +//#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */ #define IP_FW_XGET 97 /* Retrieve configuration */ #define IP_FW_XADD 98 /* add entry */ @@ -641,7 +641,12 @@ struct _ipfw_dyn_rule { #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 */ +#define IPFW_TABLE_U32 3 /* Table for holidng ports/uid/gid/etc */ +#define IPFW_TABLE_MAXTYPE 3 /* Maximum valid number */ + +#define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */ +#define IPFW_VTYPE_IP 2 /* Nexthop IP address */ +#define IPFW_VTYPE_DSCP 3 /* DiffServ codepoints */ typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ @@ -683,23 +688,45 @@ typedef struct _ipfw_xtable { typedef struct _ipfw_obj_tlv { uint16_t type; /* TLV type */ - uint16_t flags; /* unused */ + uint16_t flags; /* TLV-specific flags */ uint32_t length; /* Total length, aligned to u64 */ } ipfw_obj_tlv; #define IPFW_TLV_TBL_NAME 1 #define IPFW_TLV_TBLNAME_LIST 2 #define IPFW_TLV_RULE_LIST 3 #define IPFW_TLV_STATE_LIST 4 +#define IPFW_TLV_TBL_ENT 5 /* Object name TLV */ typedef struct _ipfw_obj_ntlv { ipfw_obj_tlv head; /* TLV header */ uint16_t idx; /* Name index */ - uint16_t spare0; /* unused */ + uint8_t spare; /* unused */ + uint8_t type; /* object type, if applicable */ uint32_t set; /* set, if applicable */ char name[64]; /* Null-terminated name */ } ipfw_obj_ntlv; +/* Table entry TLV */ +typedef struct _ipfw_obj_tentry { + ipfw_obj_tlv head; /* TLV header */ + uint8_t subtype; /* subtype (IPv4,IPv6) */ + uint8_t masklen; /* mask length */ + uint16_t idx; /* Table name index */ + uint16_t flags; /* Entry flags */ + uint16_t spare0; + uint32_t spare1; + uint32_t value; /* value */ + union { + /* Longest field needs to be aligned by 8-byte boundary */ + struct in_addr addr; /* IPv4 address */ + uint32_t key; /* uid/gid/port */ + struct in6_addr addr6; /* IPv6 address */ + char iface[IF_NAMESIZE]; /* interface name */ + } k; +} ipfw_obj_tentry; +#define IPFW_TF_UPDATE 0x01 /* Update record if exists */ + /* Containter TLVs */ typedef struct _ipfw_obj_ctlv { ipfw_obj_tlv head; /* TLV header */ @@ -710,8 +737,8 @@ typedef struct _ipfw_obj_ctlv { typedef struct _ipfw_xtable_info { uint8_t type; /* table type (cidr,iface,..) */ uint8_t ftype; /* table value format type */ - uint8_t atype; /* algorithm type */ - uint8_t spare0; + uint8_t vtype; /* value type */ + uint16_t spare0; uint32_t set; /* set table is in */ uint32_t kidx; /* kernel index */ uint32_t refcnt; /* number of references */ @@ -733,7 +760,7 @@ typedef struct _ipfw_obj_header { typedef struct _ipfw_obj_lheader { ip_fw3_opheader opheader; /* IP_FW3 opcode */ - uint32_t spare; + uint32_t set_mask; /* disabled set mask */ uint32_t count; /* Total objects count */ uint32_t size; /* Total objects size */ uint32_t objsize; /* Size of one object */ @@ -743,7 +770,7 @@ typedef struct _ipfw_obj_lheader { #define IPFW_CFG_GET_STATES 2 typedef struct _ipfw_cfg_lheader { ip_fw3_opheader opheader; /* IP_FW3 opcode */ - uint32_t set_mask; /* disabled set mask */ + uint32_t set_mask; /* enabled set mask */ uint32_t flags; /* Request flags */ uint32_t size; /* neded buffer size */ uint32_t start_rule; diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 271007a02289..f69e885ca646 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -178,7 +178,10 @@ SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD, "The default/max possible rule number."); SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max, CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU", - "Maximum number of tables"); + "Maximum number of concurrently used tables"); +SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, tables_sets, + CTLFLAG_RW, &VNET_NAME(fw_tables_sets), 0, + "Use per-set namespace for tables"); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN, &default_to_accept, 0, "Make the default rule accept all packets."); diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index 213d7890be3e..90859174a51d 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -310,15 +310,6 @@ struct sockopt_data { #define IPFW_UH_WLOCK(p) rw_wlock(&(p)->uh_lock) #define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock) -struct tid_info { - uint32_t set; /* table set */ - uint16_t uidx; /* table index */ - uint8_t type; /* table type */ - uint8_t atype; - void *tlvs; /* Pointer to first TLV */ - int tlen; /* Total TLV size block */ -}; - struct obj_idx { uint16_t uidx; /* internal index supplied by userland */ uint16_t kidx; /* kernel object index */ @@ -330,7 +321,6 @@ struct obj_idx { struct rule_check_info { uint16_t table_opcodes; /* count of opcodes referencing table */ uint16_t new_tables; /* count of opcodes referencing table */ - uint32_t tableset; /* ipfw set id for table */ ipfw_obj_ctlv *ctlv; /* name TLV containter */ struct ip_fw *krule; /* resulting rule pointer */ struct ip_fw *urule; /* original rule pointer */ @@ -373,8 +363,8 @@ void ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void ipfw_objhash_bitmap_free(void *idx, int blocks); struct named_object *ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name); -struct named_object *ipfw_objhash_lookup_idx(struct namedobj_instance *ni, - uint32_t set, uint16_t idx); +struct named_object *ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, + uint16_t idx); int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a, struct named_object *b); void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no); @@ -382,9 +372,8 @@ void ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no); uint32_t ipfw_objhash_count(struct namedobj_instance *ni); void ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg); -int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, - uint16_t idx); -int ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx); +int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx); +int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx); /* In ip_fw_table.c */ struct table_info; diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index fde823daa11f..178214f13936 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -85,8 +85,7 @@ struct namedobj_instance { static uint32_t objhash_hash_name(struct namedobj_instance *ni, uint32_t set, char *name); -static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t set, - uint32_t val); +static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t val); static int ipfw_flush_sopt_data(struct sockopt_data *sd); @@ -1018,21 +1017,25 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) dst = (struct ip_fw *)bp; bcopy(rule, dst, l); error = ipfw_rewrite_table_kidx(chain, dst); - if (error != 0) { - printf("Stop on rule %d. Fail to convert table\n", - rule->rulenum); - break; - } /* * XXX HACK. Store the disable mask in the "next" * pointer in a wild attempt to keep the ABI the same. * Why do we do this on EVERY rule? + * + * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask + * so we need to fail _after_ saving at least one mask. */ bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable)); if (dst->timestamp) dst->timestamp += boot_seconds; bp += l; + + if (error != 0) { + printf("Stop on rule %d. Fail to convert table\n", + rule->rulenum); + break; + } } ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */ return (bp - (char *)buf); @@ -1146,9 +1149,9 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) if (hdr == NULL) return (EINVAL); - /* Allocate needed state */ error = 0; bmask = NULL; + /* Allocate needed state */ if (hdr->flags & IPFW_CFG_GET_STATIC) bmask = malloc(IPFW_TABLES_MAX / 8, M_TEMP, M_WAITOK | M_ZERO); @@ -1194,7 +1197,7 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) /* Fill header anyway */ hdr->size = sz; - hdr->set_mask = V_set_disable; + hdr->set_mask = ~V_set_disable; if (sd->valsize < sz) { IPFW_UH_RUNLOCK(chain); @@ -1229,13 +1232,15 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) static int check_object_name(ipfw_obj_ntlv *ntlv) { + int error; - if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) - return (EINVAL); - - /* - * TODO: do some more complicated checks - */ + switch (ntlv->head.type) { + case IPFW_TLV_TBL_NAME: + error = ipfw_check_table_name(ntlv->name); + break; + default: + error = ENOTSUP; + } return (0); } @@ -1443,7 +1448,7 @@ ipfw_ctl(struct sockopt *sopt) { #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error; - size_t bsize_max, size, len, valsize; + size_t bsize_max, size, valsize; struct ip_fw *buf, *rule; struct ip_fw_chain *chain; u_int32_t rulenum[2]; @@ -1683,36 +1688,13 @@ ipfw_ctl(struct sockopt *sopt) /*--- TABLE opcodes ---*/ case IP_FW_TABLE_XCREATE: /* IP_FW3 */ - case IP_FW_TABLE_XMODIFY: /* IP_FW3 */ - if (opt == IP_FW_TABLE_XCREATE) - error = ipfw_create_table(chain, sopt, op3); - else - error= ipfw_modify_table(chain, sopt, op3); + error = ipfw_create_table(chain, op3, &sdata); break; case IP_FW_TABLE_XDESTROY: /* IP_FW3 */ case IP_FW_TABLE_XFLUSH: /* IP_FW3 */ - { - struct _ipfw_obj_header *oh; - struct tid_info ti; - - if (sopt->sopt_valsize < sizeof(*oh)) { - error = EINVAL; - break; - } - - oh = (struct _ipfw_obj_header *)op3; - - objheader_to_ti(oh, &ti); - - if (opt == IP_FW_TABLE_XDESTROY) - error = ipfw_destroy_table(chain, &ti); - else if (opt == IP_FW_TABLE_XFLUSH) - error = ipfw_flush_table(chain, &ti); - else - error = ENOTSUP; - break; - } + error = ipfw_flush_table(chain, op3, &sdata); + break; case IP_FW_TABLE_XINFO: /* IP_FW3 */ error = ipfw_describe_table(chain, &sdata); @@ -1732,39 +1714,7 @@ ipfw_ctl(struct sockopt *sopt) case IP_FW_TABLE_XADD: /* IP_FW3 */ case IP_FW_TABLE_XDEL: /* IP_FW3 */ - { - ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1); - struct tentry_info tei; - struct tid_info ti; - - /* Check minimum header size */ - if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) { - error = EINVAL; - break; - } - - /* Check if len field is valid */ - if (xent->len > sizeof(ipfw_table_xentry)) { - error = EINVAL; - break; - } - - len = xent->len - offsetof(ipfw_table_xentry, k); - - memset(&tei, 0, sizeof(tei)); - tei.paddr = &xent->k; - tei.plen = len; - tei.masklen = xent->masklen; - tei.value = xent->value; - memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ - ti.uidx = xent->tbl; - ti.type = xent->type; - - error = (opt == IP_FW_TABLE_XADD) ? - ipfw_add_table_entry(chain, &ti, &tei) : - ipfw_del_table_entry(chain, &ti, &tei); - } + error = ipfw_modify_table(chain, op3, &sdata); break; /*--- LEGACY API ---*/ @@ -1782,11 +1732,10 @@ ipfw_ctl(struct sockopt *sopt) memset(&tei, 0, sizeof(tei)); tei.paddr = &ent.addr; - tei.plen = sizeof(ent.addr); + tei.subtype = AF_INET; tei.masklen = ent.masklen; tei.value = ent.value; memset(&ti, 0, sizeof(ti)); - ti.set = RESVD_SET; ti.uidx = ent.tbl; ti.type = IPFW_TABLE_CIDR; @@ -1807,7 +1756,6 @@ ipfw_ctl(struct sockopt *sopt) if (error) break; memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ ti.uidx = tbl; error = ipfw_flush_table(chain, &ti); } @@ -1822,7 +1770,6 @@ ipfw_ctl(struct sockopt *sopt) sizeof(tbl)))) break; memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ ti.uidx = tbl; IPFW_RLOCK(chain); error = ipfw_count_table(chain, &ti, &cnt); @@ -1852,7 +1799,6 @@ ipfw_ctl(struct sockopt *sopt) tbl->size = (size - sizeof(*tbl)) / sizeof(ipfw_table_entry); memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ ti.uidx = tbl->tbl; IPFW_RLOCK(chain); error = ipfw_dump_table_legacy(chain, &ti, tbl); @@ -1879,7 +1825,6 @@ ipfw_ctl(struct sockopt *sopt) tbl = (uint32_t *)(op3 + 1); memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ ti.uidx = *tbl; IPFW_UH_RLOCK(chain); error = ipfw_count_xtable(chain, &ti, tbl); @@ -2249,7 +2194,7 @@ objhash_hash_name(struct namedobj_instance *ni, uint32_t set, char *name) } static uint32_t -objhash_hash_val(struct namedobj_instance *ni, uint32_t set, uint32_t val) +objhash_hash_val(struct namedobj_instance *ni, uint32_t val) { uint32_t v; @@ -2275,16 +2220,15 @@ ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name) } struct named_object * -ipfw_objhash_lookup_idx(struct namedobj_instance *ni, uint32_t set, - uint16_t idx) +ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx) { struct named_object *no; uint32_t hash; - hash = objhash_hash_val(ni, set, idx); + hash = objhash_hash_val(ni, kidx); TAILQ_FOREACH(no, &ni->values[hash], nv_next) { - if ((no->kidx == idx) && (no->set == set)) + if (no->kidx == kidx) return (no); } @@ -2310,7 +2254,7 @@ ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no) hash = objhash_hash_name(ni, no->set, no->name); TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next); - hash = objhash_hash_val(ni, no->set, no->kidx); + hash = objhash_hash_val(ni, no->kidx); TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next); ni->count++; @@ -2324,7 +2268,7 @@ ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no) hash = objhash_hash_name(ni, no->set, no->name); TAILQ_REMOVE(&ni->names[hash], no, nn_next); - hash = objhash_hash_val(ni, no->set, no->kidx); + hash = objhash_hash_val(ni, no->kidx); TAILQ_REMOVE(&ni->values[hash], no, nv_next); ni->count--; @@ -2358,7 +2302,7 @@ ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg) * Returns 0 on success. */ int -ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx) +ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx) { u_long *mask; int i, v; @@ -2366,10 +2310,10 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx) i = idx / BLOCK_ITEMS; v = idx % BLOCK_ITEMS; - if ((i >= ni->max_blocks) || set >= IPFW_MAX_SETS) + if (i >= ni->max_blocks) return (1); - mask = &ni->idx_mask[set * ni->max_blocks + i]; + mask = &ni->idx_mask[i]; if ((*mask & ((u_long)1 << v)) != 0) return (1); @@ -2378,8 +2322,8 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx) *mask |= (u_long)1 << v; /* Update free offset */ - if (ni->free_off[set] > i) - ni->free_off[set] = i; + if (ni->free_off[0] > i) + ni->free_off[0] = i; return (0); } @@ -2389,19 +2333,16 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx) * Returns 0 on success. */ int -ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx) +ipfw_objhash_alloc_idx(void *n, uint16_t *pidx) { struct namedobj_instance *ni; u_long *mask; int i, off, v; - if (set >= IPFW_MAX_SETS) - return (-1); - ni = (struct namedobj_instance *)n; - off = ni->free_off[set]; - mask = &ni->idx_mask[set * ni->max_blocks + off]; + off = ni->free_off[0]; + mask = &ni->idx_mask[off]; for (i = off; i < ni->max_blocks; i++, mask++) { if ((v = ffsl(*mask)) == 0) @@ -2410,7 +2351,7 @@ ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx) /* Mark as busy */ *mask &= ~ ((u_long)1 << (v - 1)); - ni->free_off[set] = i; + ni->free_off[0] = i; v = BLOCK_ITEMS * i + v - 1; diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index 506a17511ba6..a1db4eac7545 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$"); /* * Lookup table support for ipfw. * - * This file containg handlers for all generic tables operations: + * This file contains handlers for all generic tables' operations: * add/del/flush entries, list/dump tables etc.. * * Table data modification is protected by both UH and runtimg lock @@ -75,7 +75,7 @@ __FBSDID("$FreeBSD$"); */ struct table_config { struct named_object no; - uint8_t ftype; /* format table type */ + uint8_t vtype; /* format table type */ uint8_t linked; /* 1 if already linked */ uint16_t spare0; uint32_t count; /* Number of records */ @@ -103,11 +103,19 @@ static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc); static void free_table_state(void **state, void **xstate, uint8_t type); static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, struct sockopt_data *sd); -static void export_table_info(struct table_config *tc, ipfw_xtable_info *i); +static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc, + ipfw_xtable_info *i); static int dump_table_xentry(void *e, void *arg); static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd); static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd); +static int ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); +static int ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); + +static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); +static int flush_table(struct ip_fw_chain *ch, struct tid_info *ti); static struct table_algo *find_table_algo(struct tables_config *tableconf, struct tid_info *ti, char *name); @@ -129,11 +137,6 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, int error; char ta_buf[128]; -#if 0 - if (ti->uidx >= V_fw_tables_max) - return (EINVAL); -#endif - IPFW_UH_WLOCK(ch); ni = CHAIN_TO_NI(ch); @@ -155,7 +158,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, IPFW_UH_WUNLOCK(ch); tc_new = NULL; - if (ta == NULL) { + if (tc == NULL) { /* Table not found. We have to create new one */ if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL) return (ENOTSUP); @@ -179,7 +182,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, ni = CHAIN_TO_NI(ch); if (tc == NULL) { - /* Check if another table was allocated by other thread */ + /* Check if another table has been allocated by other thread */ if ((tc = find_table(ni, ti)) != NULL) { /* @@ -189,28 +192,27 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, */ if (tc->ta != ta) { IPFW_UH_WUNLOCK(ch); - free_table_config(ni, tc); - return (EINVAL); + error = EINVAL; + goto done; } } else { - /* - * We're first to create this table. - * Set tc_new to zero not to free it afterwards. - */ - tc = tc_new; - tc_new = NULL; + /* Table still does not exists */ /* Allocate table index. */ - if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) { + if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { /* Index full. */ IPFW_UH_WUNLOCK(ch); - printf("Unable to allocate index for table %s." - " Consider increasing " + printf("Unable to allocate index for table %s" + "in set %u. Consider increasing " "net.inet.ip.fw.tables_max", - tc->no.name); - free_table_config(ni, tc); - return (EBUSY); + tc_new->no.name, ti->set); + error = EBUSY; + goto done; } + + /* Set tc_new to zero not to free it afterwards. */ + tc = tc_new; + tc_new = NULL; /* Save kidx */ tc->no.kidx = kidx; } @@ -225,24 +227,24 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, IPFW_WLOCK(ch); - if (tc->linked == 0) { + if (tc->linked == 0) link_table(ch, tc); - } error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf); IPFW_WUNLOCK(ch); - if (error == 0) + /* Update number of records. */ + if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0) tc->count++; IPFW_UH_WUNLOCK(ch); +done: if (tc_new != NULL) - free_table_config(ni, tc); - - if (error != 0) - ta->flush_entry(tei, &ta_buf); + free_table_config(ni, tc_new); + /* Run cleaning callback anyway */ + ta->flush_entry(tei, &ta_buf); return (error); } @@ -289,18 +291,176 @@ ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, IPFW_UH_WUNLOCK(ch); - if (error != 0) - return (error); - ta->flush_entry(tei, &ta_buf); - return (0); + + return (error); +} + +int +ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + int error; + + switch (op3->version) { + case 0: + error = ipfw_modify_table_v0(ch, op3, sd); + break; + case 1: + error = ipfw_modify_table_v1(ch, op3, sd); + break; + default: + error = ENOTSUP; + } + + return (error); +} + +/* + * Adds or deletes record in table. + * Data layout (v0): + * Request: [ ip_fw3_opheader ipfw_table_xentry ] + * + * Returns 0 on success + */ +static int +ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_table_xentry *xent; + struct tentry_info tei; + struct tid_info ti; + int error, hdrlen, read; + + hdrlen = offsetof(ipfw_table_xentry, k); + + /* Check minimum header size */ + if (sd->valsize < (sizeof(*op3) + hdrlen)) + return (EINVAL); + + read = sizeof(ip_fw3_opheader); + + /* Check if xentry len field is valid */ + xent = (ipfw_table_xentry *)(op3 + 1); + if (xent->len < hdrlen || xent->len + read > sd->valsize) + return (EINVAL); + + memset(&tei, 0, sizeof(tei)); + tei.paddr = &xent->k; + tei.masklen = xent->masklen; + tei.value = xent->value; + /* Old requests compability */ + if (xent->type == IPFW_TABLE_CIDR) { + if (xent->len - hdrlen == sizeof(in_addr_t)) + tei.subtype = AF_INET; + else + tei.subtype = AF_INET6; + } + + memset(&ti, 0, sizeof(ti)); + ti.uidx = xent->tbl; + ti.type = xent->type; + + error = (op3->opcode == IP_FW_TABLE_XADD) ? + ipfw_add_table_entry(ch, &ti, &tei) : + ipfw_del_table_entry(ch, &ti, &tei); + + return (error); +} + +/* + * Adds or deletes record in table. + * Data layout (v1)(current): + * Request: [ ipfw_obj_header ipfw_obj_tentry ] + * + * Returns 0 on success + */ +static int +ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_tentry *tent; + ipfw_obj_header *oh; + struct tentry_info tei; + struct tid_info ti; + int error, read; + + /* Check minimum header size */ + if (sd->valsize < (sizeof(*oh) + sizeof(*tent))) + return (EINVAL); + + /* Check if passed data is too long */ + if (sd->valsize != sd->kavail) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + read = sizeof(*oh); + + /* Assume tentry may grow to support larger keys */ + tent = (ipfw_obj_tentry *)(oh + 1); + if (tent->head.length < sizeof(*tent) || + tent->head.length + read > sd->valsize) + return (EINVAL); + + memset(&tei, 0, sizeof(tei)); + tei.paddr = &tent->k; + tei.subtype = tent->subtype; + tei.masklen = tent->masklen; + if (tent->flags & IPFW_TF_UPDATE) + tei.flags |= TEI_FLAGS_UPDATE; + tei.value = tent->value; + + memset(&ti, 0, sizeof(ti)); + ti.uidx = tent->idx; + ti.type = oh->ntlv.type; + ti.tlvs = &oh->ntlv; + ti.tlen = oh->ntlv.head.length; + + error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ? + ipfw_add_table_entry(ch, &ti, &tei) : + ipfw_del_table_entry(ch, &ti, &tei); + + return (error); +} + +int +ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + int error; + struct _ipfw_obj_header *oh; + struct tid_info ti; + + if (sd->valsize != sizeof(*oh)) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)op3; + objheader_to_ti(oh, &ti); + + if (opt == IP_FW_TABLE_XDESTROY) + error = destroy_table(ch, &ti); + else if (opt == IP_FW_TABLE_XFLUSH) + error = flush_table(ch, &ti); + else + return (ENOTSUP); + + return (error); } /* * Flushes all entries in given table. + * Data layout (v0)(current): + * Request: [ ip_fw3_opheader ] + * + * Returns 0 on success */ -int -ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti) +static int +flush_table(struct ip_fw_chain *ch, struct tid_info *ti) { struct namedobj_instance *ni; struct table_config *tc; @@ -369,15 +529,17 @@ ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti) /* * Destroys table specified by @ti. + * Data layout (v0)(current): + * Request: [ ip_fw3_opheader ] + * + * Returns 0 on success */ -int -ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) +static int +destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) { struct namedobj_instance *ni; struct table_config *tc; - ti->set = TABLE_SET(ti->set); - IPFW_UH_WLOCK(ch); ni = CHAIN_TO_NI(ch); @@ -397,7 +559,7 @@ ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti) IPFW_WUNLOCK(ch); /* Free obj index */ - if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0) + if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0) printf("Error unlinking kidx %d from table %s\n", tc->no.kidx, tc->tablename); @@ -414,7 +576,7 @@ destroy_table_locked(struct namedobj_instance *ni, struct named_object *no, { unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no); - if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0) + if (ipfw_objhash_free_idx(ni, no->kidx) != 0) printf("Error unlinking kidx %d from table %s\n", no->kidx, no->name); free_table_config(ni, (struct table_config *)no); @@ -548,7 +710,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen, /* * Get buffer size needed to list info for all tables. - * Data layout: + * Data layout (v0)(current): * Request: [ empty ], size = sizeof(ipfw_obj_lheader) * Reply: [ ipfw_obj_lheader ] * @@ -574,7 +736,7 @@ ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) /* * Lists all tables currently available in kernel. - * Data layout: + * Data layout (v0)(current): * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ] * @@ -607,7 +769,7 @@ ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd) /* * Store table info to buffer provided by @sd. - * Data layout: + * Data layout (v0)(current): * Request: [ ipfw_obj_header ipfw_xtable_info(empty)] * Reply: [ ipfw_obj_header ipfw_xtable_info ] * @@ -634,7 +796,7 @@ ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd) return (ESRCH); } - export_table_info(tc, (ipfw_xtable_info *)(oh + 1)); + export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1)); IPFW_UH_RUNLOCK(ch); return (0); @@ -672,7 +834,7 @@ ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, /* * Dumps all table data - * Data layout (version 1)(current): + * Data layout (v1)(current): * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ] * @@ -702,7 +864,7 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd) IPFW_UH_RUNLOCK(ch); return (ESRCH); } - export_table_info(tc, i); + export_table_info(ch, tc, i); sz = tc->count; if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) { @@ -756,7 +918,6 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) return (EINVAL); memset(&ti, 0, sizeof(ti)); - ti.set = 0; /* XXX: No way to specify set */ ti.uidx = xtbl->tbl; IPFW_UH_RLOCK(ch); @@ -797,27 +958,16 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd) return (0); } -/* - * High-level setsockopt cmds - */ -int -ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt, - ip_fw3_opheader *op3) -{ - - return (ENOTSUP); -} - /* * Creates new table. - * Data layout: + * Data layout (v0)(current): * Request: [ ipfw_obj_header ipfw_xtable_info ] * * Returns 0 on success */ int -ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, - ip_fw3_opheader *op3) +ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd) { struct _ipfw_obj_header *oh; ipfw_xtable_info *i; @@ -828,20 +978,19 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, struct table_algo *ta; uint16_t kidx; - if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info)) + if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info)) return (EINVAL); - oh = (struct _ipfw_obj_header *)op3; + oh = (struct _ipfw_obj_header *)sd->kbuf; i = (ipfw_xtable_info *)(oh + 1); /* * Verify user-supplied strings. * Check for null-terminated/zero-length strings/ */ - tname = i->tablename; + tname = oh->ntlv.name; aname = i->algoname; - if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) || - tname[0] == '\0' || + if (ipfw_check_table_name(tname) != 0 || strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname)) return (EINVAL); @@ -851,6 +1000,9 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, } objheader_to_ti(oh, &ti); + /* Create table in set 0 by default */ + ti->set = TABLE_SET(ti->set); + ti.type = i->type; ni = CHAIN_TO_NI(ch); @@ -867,9 +1019,11 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL) return (ENOMEM); + /* TODO: move inside alloc_table_config() */ + tc->vtype = i->vtype; IPFW_UH_WLOCK(ch); - if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) { + if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) { IPFW_UH_WUNLOCK(ch); printf("Unable to allocate table index for table %s in set %u." " Consider increasing net.inet.ip.fw.tables_max", @@ -910,7 +1064,7 @@ ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, ni = CHAIN_TO_NI(ch); - no = ipfw_objhash_lookup_idx(ni, 0, kidx); + no = ipfw_objhash_lookup_kidx(ni, kidx); KASSERT(no != NULL, ("invalid table kidx passed")); ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); @@ -926,12 +1080,13 @@ ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, } static void -export_table_info(struct table_config *tc, ipfw_xtable_info *i) +export_table_info(struct ip_fw_chain *ch, struct table_config *tc, + ipfw_xtable_info *i) { + struct table_info *ti; i->type = tc->no.type; - i->ftype = tc->ftype; - i->atype = tc->ta->idx; + i->vtype = tc->vtype; i->set = tc->no.set; i->kidx = tc->no.kidx; i->refcnt = tc->no.refcnt; @@ -939,20 +1094,33 @@ export_table_info(struct table_config *tc, ipfw_xtable_info *i) i->size = tc->count * sizeof(ipfw_table_xentry); i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info); strlcpy(i->tablename, tc->tablename, sizeof(i->tablename)); + if (tc->ta->print_config != NULL) { + /* Use algo function to print table config to string */ + ti = KIDX_TO_TI(ch, tc->no.kidx); + tc->ta->print_config(tc->astate, ti, i->algoname, + sizeof(i->algoname)); + } else + strlcpy(i->algoname, tc->ta->name, sizeof(i->algoname)); } +struct dump_table_args { + struct ip_fw_chain *ch; + struct sockopt_data *sd; +}; + static void export_table_internal(struct namedobj_instance *ni, struct named_object *no, void *arg) { ipfw_xtable_info *i; - struct sockopt_data *sd; + struct dump_table_args *dta; - sd = (struct sockopt_data *)arg; - i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i)); + dta = (struct dump_table_args *)arg; + + i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i)); KASSERT(i == 0, ("previously checked buffer is not enough")); - export_table_info((struct table_config *)no, i); + export_table_info(dta->ch, (struct table_config *)no, i); } /* @@ -966,6 +1134,7 @@ export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, { uint32_t size; uint32_t count; + struct dump_table_args dta; count = ipfw_objhash_count(CHAIN_TO_NI(ch)); size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader); @@ -981,7 +1150,10 @@ export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh, } olh->size = size; - ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd); + dta.ch = ch; + dta.sd = sd; + + ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta); return (0); } @@ -1264,7 +1436,41 @@ update_table_opcode(ipfw_insn *cmd, uint16_t idx) } } -static char * +/* + * Checks table name for validity. + * Enforce basic length checks, the rest + * should be done in userland. + * + * Returns 0 if name is considered valid. + */ +int +ipfw_check_table_name(char *name) +{ + int nsize; + ipfw_obj_ntlv *ntlv = NULL; + + nsize = sizeof(ntlv->name); + + if (strnlen(name, nsize) == nsize) + return (EINVAL); + + if (name[0] == '\0') + return (EINVAL); + + /* + * TODO: do some more complicated checks + */ + + return (0); +} + +/* + * Find tablename TLV by @uid. + * Check @tlvs for valid data inside. + * + * Returns pointer to found TLV or NULL. + */ +static ipfw_obj_ntlv * find_name_tlv(void *tlvs, int len, uint16_t uidx) { ipfw_obj_ntlv *ntlv; @@ -1277,33 +1483,53 @@ find_name_tlv(void *tlvs, int len, uint16_t uidx) for (; pa < pe; pa += l) { ntlv = (ipfw_obj_ntlv *)pa; l = ntlv->head.length; + + if (l != sizeof(*ntlv)) + return (NULL); + if (ntlv->head.type != IPFW_TLV_TBL_NAME) continue; + if (ntlv->idx != uidx) continue; + + if (ipfw_check_table_name(ntlv->name) != 0) + return (NULL); - return (ntlv->name); + return (ntlv); } return (NULL); } +/* + * Finds table config based on either legacy index + * or name in ntlv. + * Note @ti structure contains unchecked data from userland. + * + * Returns pointer to table_config or NULL. + */ static struct table_config * find_table(struct namedobj_instance *ni, struct tid_info *ti) { char *name, bname[16]; struct named_object *no; + ipfw_obj_ntlv *ntlv; + uint32_t set; if (ti->tlvs != NULL) { - name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); - if (name == NULL) + ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); + if (ntlv == NULL) return (NULL); + name = ntlv->name; + set = ntlv->set; } else { snprintf(bname, sizeof(bname), "%d", ti->uidx); name = bname; + set = 0; } - no = ipfw_objhash_lookup_name(ni, ti->set, name); + no = ipfw_objhash_lookup_name(ni, set, name); return ((struct table_config *)no); } @@ -1315,22 +1541,29 @@ alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti, char *name, bname[16]; struct table_config *tc; int error; + ipfw_obj_ntlv *ntlv; + uint32_t set; if (ti->tlvs != NULL) { - name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); - if (name == NULL) + ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx); + if (ntlv == NULL) return (NULL); + name = ntlv->name; + set = ntlv->set; } else { snprintf(bname, sizeof(bname), "%d", ti->uidx); name = bname; + set = 0; } tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO); tc->no.name = tc->tablename; tc->no.type = ti->type; - tc->no.set = ti->set; + tc->no.set = set; tc->ta = ta; strlcpy(tc->tablename, name, sizeof(tc->tablename)); + /* Set default value type to u32 for compability reasons */ + tc->vtype = IPFW_VTYPE_U32; if (ti->tlvs == NULL) { tc->no.compat = 1; @@ -1458,10 +1691,10 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, } /* Table not found. Allocate new index and save for later */ - if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) { - printf("Unable to allocate table index in set %u." + if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) { + printf("Unable to allocate table %s index in set %u." " Consider increasing net.inet.ip.fw.tables_max", - ti->set); + "", ti->set); error = EBUSY; break; } @@ -1475,12 +1708,12 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, /* Unref everything we have already done */ for (p = *oib; p < pidx; p++) { if (p->new != 0) { - ipfw_objhash_free_idx(ni, ci->tableset,p->kidx); + ipfw_objhash_free_idx(ni, p->kidx); continue; } /* Find & unref by existing idx */ - no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx); + no = ipfw_objhash_lookup_kidx(ni, p->kidx); KASSERT(no != NULL, ("Ref'd table %d disappeared", p->kidx)); @@ -1527,7 +1760,7 @@ ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule) if (classify_table_opcode(cmd, &kidx, &type) != 0) continue; - if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL) + if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL) return (1); if (no->compat == 0) @@ -1619,10 +1852,8 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, type = 0; ftype = 0; - ci->tableset = TABLE_SET(ci->krule->set); - memset(&ti, 0, sizeof(ti)); - ti.set = ci->tableset; + ti.set = TABLE_SET(ci->krule->set); if (ci->ctlv != NULL) { ti.tlvs = (void *)(ci->ctlv + 1); ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv); @@ -1650,7 +1881,6 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, if (p->new == 0) continue; - /* TODO: get name from TLV */ ti.uidx = p->uidx; ti.type = p->type; ti.atype = 0; @@ -1785,7 +2015,7 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, /* Free indexes first */ IPFW_UH_WLOCK(chain); TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) { - ipfw_objhash_free_idx(ni, ci->tableset, no->kidx); + ipfw_objhash_free_idx(ni, no->kidx); } IPFW_UH_WUNLOCK(chain); /* Free configs */ @@ -1826,7 +2056,7 @@ ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule) if (classify_table_opcode(cmd, &kidx, &type) != 0) continue; - no = ipfw_objhash_lookup_idx(ni, set, kidx); + no = ipfw_objhash_lookup_kidx(ni, kidx); KASSERT(no != NULL, ("table id %d not found", kidx)); KASSERT(no->type == type, ("wrong type %d (%d) for table id %d", diff --git a/sys/netpfil/ipfw/ip_fw_table.h b/sys/netpfil/ipfw/ip_fw_table.h index 4d0d3cedcc34..b3c967882574 100644 --- a/sys/netpfil/ipfw/ip_fw_table.h +++ b/sys/netpfil/ipfw/ip_fw_table.h @@ -41,14 +41,26 @@ struct table_info { u_long data; /* Hints for given func */ }; + +/* Internal structures for handling sockopt data */ +struct tid_info { + uint32_t set; /* table set */ + uint16_t uidx; /* table index */ + uint8_t type; /* table type */ + uint8_t atype; + void *tlvs; /* Pointer to first TLV */ + int tlen; /* Total TLV size block */ +}; + struct tentry_info { void *paddr; - int plen; /* Total entry length */ uint8_t masklen; /* mask length */ - uint8_t spare; + uint8_t subtype; uint16_t flags; /* record flags */ uint32_t value; /* value */ }; +#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */ +#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */ typedef int (ta_init)(void **ta_state, struct table_info *ti, char *data); typedef void (ta_destroy)(void *ta_state, struct table_info *ti); @@ -59,6 +71,8 @@ typedef int (ta_add)(void *ta_state, struct table_info *ti, typedef int (ta_del)(void *ta_state, struct table_info *ti, struct tentry_info *tei, void *ta_buf); typedef void (ta_flush_entry)(struct tentry_info *tei, void *ta_buf); +typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf, + size_t bufsize); typedef int ta_foreach_f(void *node, void *arg); typedef void ta_foreach(void *ta_state, struct table_info *ti, ta_foreach_f *f, @@ -82,6 +96,7 @@ struct table_algo { ta_foreach *foreach; ta_dump_entry *dump_entry; ta_dump_xentry *dump_xentry; + ta_print_config *print_config; }; void ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta); extern struct table_algo radix_cidr, radix_iface; @@ -97,17 +112,17 @@ int ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd); -int ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt, - ip_fw3_opheader *op3); -int ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt, - ip_fw3_opheader *op3); - -int ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); -int ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti); +int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); +int ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); int ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei); int ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei); + +int ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti); +int ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti); int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci); int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule); @@ -120,6 +135,7 @@ void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head); /* utility functions */ void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti); +int ipfw_check_table_name(char *name); /* Legacy interfaces */ int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, diff --git a/sys/netpfil/ipfw/ip_fw_table_algo.c b/sys/netpfil/ipfw/ip_fw_table_algo.c index ec97629de022..4ad0466ff6fd 100644 --- a/sys/netpfil/ipfw/ip_fw_table_algo.c +++ b/sys/netpfil/ipfw/ip_fw_table_algo.c @@ -324,12 +324,13 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf) memset(tb, 0, sizeof(struct ta_buf_cidr)); mlen = tei->masklen; - - if (tei->plen == sizeof(in_addr_t)) { + + if (tei->subtype == AF_INET) { #ifdef INET /* IPv4 case */ if (mlen > 32) return (EINVAL); + ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); ent->value = tei->value; /* Set 'total' structure length */ @@ -346,7 +347,7 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf) tb->mask_ptr = (struct sockaddr *)&ent->mask; #endif #ifdef INET6 - } else if (tei->plen == sizeof(struct in6_addr)) { + } else if (tei->subtype == AF_INET6) { /* IPv6 case */ if (mlen > 128) return (EINVAL); @@ -380,18 +381,46 @@ ta_add_cidr(void *ta_state, struct table_info *ti, struct radix_node_head *rnh; struct radix_node *rn; struct ta_buf_cidr *tb; + uint32_t value; tb = (struct ta_buf_cidr *)ta_buf; - if (tei->plen == sizeof(in_addr_t)) + if (tei->subtype == AF_INET) rnh = ti->state; else rnh = ti->xstate; rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr); - if (rn == NULL) - return (EEXIST); + if (rn == NULL) { + if ((tei->flags & TEI_FLAGS_UPDATE) == 0) + return (EEXIST); + /* Record already exists. Update value if we're asked to */ + rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh); + if (rn == NULL) { + + /* + * Radix may have failed addition for other reasons + * like failure in mask allocation code. + */ + return (EINVAL); + } + + if (tei->subtype == AF_INET) { + /* IPv4. */ + value = ((struct table_entry *)tb->ent_ptr)->value; + ((struct table_entry *)rn)->value = value; + } else { + /* IPv6 */ + value = ((struct table_xentry *)tb->ent_ptr)->value; + ((struct table_xentry *)rn)->value = value; + } + + /* Indicate that update has happened instead of addition */ + tei->flags |= TEI_FLAGS_UPDATED; + } + + tb->ent_ptr = NULL; return (0); } @@ -408,7 +437,7 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf) mlen = tei->masklen; - if (tei->plen == sizeof(in_addr_t)) { + if (tei->subtype == AF_INET) { /* Set 'total' structure length */ struct sockaddr_in sa, mask; KEY_LEN(sa) = KEY_LEN_INET; @@ -421,7 +450,7 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf) tb->addr_ptr = (struct sockaddr *)&tb->addr.a4.sa; tb->mask_ptr = (struct sockaddr *)&tb->addr.a4.ma; #ifdef INET6 - } else if (tei->plen == sizeof(struct in6_addr)) { + } else if (tei->subtype == AF_INET6) { /* IPv6 case */ if (mlen > 128) return (EINVAL); @@ -456,7 +485,7 @@ ta_del_cidr(void *ta_state, struct table_info *ti, tb = (struct ta_buf_cidr *)ta_buf; - if (tei->plen == sizeof(in_addr_t)) + if (tei->subtype == AF_INET) rnh = ti->state; else rnh = ti->xstate; @@ -478,11 +507,12 @@ ta_flush_cidr_entry(struct tentry_info *tei, void *ta_buf) tb = (struct ta_buf_cidr *)ta_buf; - free(tb->ent_ptr, M_IPFW_TBL); + if (tb->ent_ptr != NULL) + free(tb->ent_ptr, M_IPFW_TBL); } struct table_algo radix_cidr = { - .name = "cidr", + .name = "radix_cidr", .lookup = ta_lookup_radix, .init = ta_init_radix, .destroy = ta_destroy_radix, @@ -557,7 +587,7 @@ ta_destroy_iface(void *ta_state, struct table_info *ti) rnh = (struct radix_node_head *)(ti->xstate); rnh->rnh_walktree(rnh, flush_iface_entry, rnh); - rn_detachhead(&ti->state); + rn_detachhead(&ti->xstate); } struct ta_buf_iface @@ -613,14 +643,31 @@ ta_add_iface(void *ta_state, struct table_info *ti, struct radix_node_head *rnh; struct radix_node *rn; struct ta_buf_iface *tb; + uint32_t value; tb = (struct ta_buf_iface *)ta_buf; rnh = ti->xstate; rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr); - if (rn == NULL) - return (1); + if (rn == NULL) { + if ((tei->flags & TEI_FLAGS_UPDATE) == 0) + return (EEXIST); + /* Record already exists. Update value if we're asked to */ + rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh); + if (rn == NULL) { + /* Radix may have failed addition for other reasons */ + return (EINVAL); + } + + value = ((struct table_xentry *)tb->ent_ptr)->value; + ((struct table_xentry *)rn)->value = value; + + /* Indicate that update has happened instead of addition */ + tei->flags |= TEI_FLAGS_UPDATED; + } + + tb->ent_ptr = NULL; return (0); } @@ -689,7 +736,8 @@ ta_flush_iface_entry(struct tentry_info *tei, void *ta_buf) tb = (struct ta_buf_iface *)ta_buf; - free(tb->ent_ptr, M_IPFW_TBL); + if (tb->ent_ptr != NULL) + free(tb->ent_ptr, M_IPFW_TBL); } static int @@ -717,7 +765,7 @@ ta_foreach_iface(void *ta_state, struct table_info *ti, ta_foreach_f *f, } struct table_algo radix_iface = { - .name = "iface", + .name = "radix_iface", .lookup = ta_lookup_iface, .init = ta_init_iface, .destroy = ta_destroy_iface,