Date: Thu, 31 Jul 2014 20:08:19 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269348 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw Message-ID: <201407312008.s6VK8J9R083960@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Thu Jul 31 20:08:19 2014 New Revision: 269348 URL: http://svnweb.freebsd.org/changeset/base/269348 Log: * Add new "flow" table type to support N=1..5-tuple lookups * Add "flow:hash" algorithm Kernel changes: * Add O_IP_FLOW_LOOKUP opcode to support "flow" lookups * Add IPFW_TABLE_FLOW table type * Add "struct tflow_entry" as strage for 6-tuple flows * Add "flow:hash" algorithm. Basically it is auto-growing chained hash table. Additionally, we store mask of fields we need to compare in each instance/ * Increase ipfw_obj_tentry size by adding struct tflow_entry * Add per-algorithm stat (ifpw_ta_tinfo) to ipfw_xtable_info * Increase algoname length: 32 -> 64 (algo options passed there as string) * Assume every table type can be customized by flags, use u8 to store "tflags" field. * Simplify ipfw_find_table_entry() by providing @tentry directly to algo callback. * Fix bug in cidr:chash resize procedure. Userland changes: * add "flow table(NAME)" syntax to support n-tuple checking tables. * make fill_flags() separate function to ease working with _s_x arrays * change "table info" output to reflect longer "type" fields Syntax: ipfw table fl2 create type flow:[src-ip][,proto][,src-port][,dst-ip][dst-port] [algo flow:hash] Examples: 0:02 [2] zfscurr0# ipfw table fl2 create type flow:src-ip,proto,dst-port algo flow:hash 0:02 [2] zfscurr0# ipfw table fl2 info +++ table(fl2), set(0) +++ kindex: 0, type: flow:src-ip,proto,dst-port valtype: number, references: 0 algorithm: flow:hash items: 0, size: 280 0:02 [2] zfscurr0# ipfw table fl2 add 2a02:6b8::333,tcp,443 45000 0:02 [2] zfscurr0# ipfw table fl2 add 10.0.0.92,tcp,80 22000 0:02 [2] zfscurr0# ipfw table fl2 list +++ table(fl2), set(0) +++ 2a02:6b8::333,6,443 45000 10.0.0.92,6,80 22000 0:02 [2] zfscurr0# ipfw add 200 count tcp from me to 78.46.89.105 80 flow 'table(fl2)' 00200 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2) 0:03 [2] zfscurr0# ipfw show 00200 0 0 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2) 65535 617 59416 allow ip from any to any 0:03 [2] zfscurr0# telnet -s 10.0.0.92 78.46.89.105 80 Trying 78.46.89.105... .. 0:04 [2] zfscurr0# ipfw show 00200 5 272 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2) 65535 682 66733 allow ip from any to any Modified: projects/ipfw/sbin/ipfw/ipfw2.c projects/ipfw/sbin/ipfw/ipfw2.h 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_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 31 19:24:44 2014 (r269347) +++ projects/ipfw/sbin/ipfw/ipfw2.c Thu Jul 31 20:08:19 2014 (r269348) @@ -364,6 +364,7 @@ static struct _s_x rule_options[] = { { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, { "lookup", TOK_LOOKUP}, + { "flow", TOK_FLOW}, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ @@ -707,6 +708,54 @@ concat_tokens(char *buf, size_t bufsize, } /* + * helper function to process a set of flags and set bits in the + * appropriate masks. + */ +void +fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear) +{ + char *q; /* points to the separator */ + int val; + uint8_t *which; /* mask we are working on */ + + while (p && *p) { + if (*p == '!') { + p++; + which = clear; + } else + which = set; + q = strchr(p, ','); + if (q) + *q++ = '\0'; + val = match_token(flags, p); + if (val <= 0) + errx(EX_DATAERR, "invalid flag %s", p); + *which |= (uint8_t)val; + p = q; + } +} + +void +print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set) +{ + char const *comma = ""; + int i, l; + + for (i = 0; list[i].x != 0; i++) { + if ((set & list[i].x) == 0) + continue; + + set &= ~list[i].x; + l = snprintf(buf, sz, "%s%s", comma, list[i].s); + if (l >= sz) + return; + comma = ","; + buf += l; + sz -=l; + } +} + +/* * _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 * of the second. A warning is printed to stderr in the case that the @@ -1087,6 +1136,7 @@ print_flags(char const *name, ipfw_insn } } + /* * Print the ip address contained in a command. */ @@ -1795,6 +1845,18 @@ show_static_rule(struct cmdline_opts *co break; } + case O_IP_FLOW_LOOKUP: + { + char *t; + + t = table_search_ctlv(fo->tstate, cmd->arg1); + printf(" flow table(%s", t); + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) + printf(",%u", + ((ipfw_insn_u32 *)cmd)->d[0]); + printf(")"); + break; + } case O_IPID: if (F_LEN(cmd) == 1) printf(" ipid %u", cmd->arg1 ); @@ -2660,6 +2722,33 @@ pack_table(struct tidx *tstate, char *na return (ntlv->idx); } +static void +fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) +{ + uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; + uint16_t uidx; + char *p; + + if ((p = strchr(av + 6, ')')) == NULL) + errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); + *p = '\0'; + p = strchr(av + 6, ','); + if (p) + *p++ = '\0'; + + if ((uidx = pack_table(tstate, av + 6, 0)) == 0) + errx(EX_DATAERR, "Invalid table name: %s", av + 6); + + cmd->opcode = opcode; + cmd->arg1 = uidx; + if (p) { + cmd->len |= F_INSN_SIZE(ipfw_insn_u32); + d[0] = strtoul(p, NULL, 0); + } else + cmd->len |= F_INSN_SIZE(ipfw_insn); +} + + /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. @@ -2676,8 +2765,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; - uint16_t uidx; - char *p; cmd->o.len &= ~F_LEN_MASK; /* zero len */ @@ -2690,23 +2777,7 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int } if (strncmp(av, "table(", 6) == 0) { - if ((p = strchr(av + 6, ')')) == NULL) - errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); - *p = '\0'; - p = strchr(av + 6, ','); - if (p) - *p++ = '\0'; - - if ((uidx = pack_table(tstate, av + 6, 0)) == 0) - errx(EX_DATAERR, "Invalid table name: %s", av + 6); - - cmd->o.opcode = O_IP_DST_LOOKUP; - cmd->o.arg1 = uidx; - if (p) { - cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); - d[0] = strtoul(p, NULL, 0); - } else - cmd->o.len |= F_INSN_SIZE(ipfw_insn); + fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); return; } @@ -2887,35 +2958,14 @@ n2mask(struct in6_addr *mask, int n) return; } -/* - * helper function to process a set of flags and set bits in the - * appropriate masks. - */ static void -fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, +fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, struct _s_x *flags, char *p) { - uint8_t set=0, clear=0; + uint8_t set = 0, clear = 0; - while (p && *p) { - char *q; /* points to the separator */ - int val; - uint8_t *which; /* mask we are working on */ + fill_flags(flags, p, &set, &clear); - if (*p == '!') { - p++; - which = &clear; - } else - which = &set; - q = strchr(p, ','); - if (q) - *q++ = '\0'; - val = match_token(flags, p); - if (val <= 0) - errx(EX_DATAERR, "invalid flag %s", p); - *which |= (uint8_t)val; - p = q; - } cmd->opcode = opcode; cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); @@ -4087,13 +4137,13 @@ read_options: case TOK_IPOPTS: NEED1("missing argument for ipoptions"); - fill_flags(cmd, O_IPOPT, f_ipopts, *av); + fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av); av++; break; case TOK_IPTOS: NEED1("missing argument for iptos"); - fill_flags(cmd, O_IPTOS, f_iptos, *av); + fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av); av++; break; @@ -4171,7 +4221,7 @@ read_options: case TOK_TCPOPTS: NEED1("missing argument for tcpoptions"); - fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); + fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av); av++; break; @@ -4198,7 +4248,7 @@ read_options: case TOK_TCPFLAGS: NEED1("missing argument for tcpflags"); cmd->opcode = O_TCPFLAGS; - fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); + fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av); av++; break; @@ -4407,6 +4457,14 @@ read_options: av++; } break; + case TOK_FLOW: + NEED1("missing table name"); + if (strncmp(*av, "table(", 6) != 0) + errx(EX_DATAERR, + "enclose table name into \"table()\""); + fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate); + av++; + break; default: errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); Modified: projects/ipfw/sbin/ipfw/ipfw2.h ============================================================================== --- projects/ipfw/sbin/ipfw/ipfw2.h Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sbin/ipfw/ipfw2.h Thu Jul 31 20:08:19 2014 (r269348) @@ -217,6 +217,7 @@ enum tokens { TOK_DEL, TOK_VALTYPE, TOK_ALGO, + TOK_FLOW, }; /* * the following macro returns an error message if we run out of @@ -253,6 +254,10 @@ int match_token(struct _s_x *table, char char const *match_value(struct _s_x *p, int value); size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter); +void fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear); +void print_flags(char const *name, struct _s_x *list, uint8_t set, + uint8_t clear); +void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set); struct _ip_fw3_opheader; int do_cmd(int optname, void *optval, uintptr_t optlen); Modified: projects/ipfw/sbin/ipfw/tables.c ============================================================================== --- projects/ipfw/sbin/ipfw/tables.c Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sbin/ipfw/tables.c Thu Jul 31 20:08:19 2014 (r269348) @@ -83,6 +83,7 @@ static struct _s_x tabletypes[] = { { "cidr", IPFW_TABLE_CIDR }, { "iface", IPFW_TABLE_INTERFACE }, { "number", IPFW_TABLE_NUMBER }, + { "flow", IPFW_TABLE_FLOW }, { NULL, 0 } }; @@ -256,6 +257,59 @@ static struct _s_x tablenewcmds[] = { { NULL, 0 } }; +static struct _s_x flowtypecmds[] = { + { "src-ip", IPFW_TFFLAG_SRCIP }, + { "proto", IPFW_TFFLAG_PROTO }, + { "src-port", IPFW_TFFLAG_SRCPORT }, + { "dst-ip", IPFW_TFFLAG_DSTIP }, + { "dst-port", IPFW_TFFLAG_DSTPORT }, + { NULL, 0 } +}; + +int +table_parse_type(uint8_t ttype, char *p, uint8_t *tflags) +{ + uint8_t fset, fclear; + + /* Parse type options */ + switch(ttype) { + case IPFW_TABLE_FLOW: + fset = fclear = 0; + fill_flags(flowtypecmds, p, &fset, + &fclear); + *tflags = fset; + break; + default: + return (EX_USAGE); + } + + return (0); +} + +void +table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags) +{ + const char *tname; + int l; + + if ((tname = match_value(tabletypes, type)) == NULL) + tname = "unknown"; + + l = snprintf(tbuf, size, "%s", tname); + tbuf += l; + size -= l; + + switch(type) { + case IPFW_TABLE_FLOW: + if (tflags != 0) { + *tbuf++ = ':'; + l--; + print_flags_buffer(tbuf, size, flowtypecmds, tflags); + } + break; + } +} + /* * Creates new table * @@ -271,6 +325,7 @@ table_create(ipfw_obj_header *oh, int ac ipfw_xtable_info xi; int error, tcmd, val; size_t sz; + char *p; char tbuf[128]; sz = sizeof(tbuf); @@ -288,15 +343,25 @@ table_create(ipfw_obj_header *oh, int ac switch (tcmd) { case TOK_TYPE: NEED1("table type required"); + /* Type may have suboptions after ':' */ + if ((p = strchr(*av, ':')) != NULL) + *p++ = '\0'; val = match_token(tabletypes, *av); - if (val != -1) { - xi.type = val; - ac--; av++; - break; + if (val == -1) { + concat_tokens(tbuf, sizeof(tbuf), tabletypes, + ", "); + errx(EX_USAGE, + "Unknown tabletype: %s. Supported: %s", + *av, tbuf); } - concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", "); - errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s", - *av, tbuf); + xi.type = val; + if (p != NULL) { + error = table_parse_type(val, p, &xi.tflags); + if (error != 0) + errx(EX_USAGE, + "Unsupported suboptions: %s", p); + } + ac--; av++; break; case TOK_VALTYPE: NEED1("table value type required"); @@ -408,15 +473,15 @@ table_get_info(ipfw_obj_header *oh, ipfw static int table_show_info(ipfw_xtable_info *i, void *arg) { - const char *ttype, *vtype; + const char *vtype; + char ttype[64]; - printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); - if ((ttype = match_value(tabletypes, i->type)) == NULL) - ttype = "unknown"; + table_print_type(ttype, sizeof(ttype), i->type, i->tflags); if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL) vtype = "unknown"; - printf(" type: %s, kindex: %d\n", ttype, i->kidx); + printf("--- table(%s), set(%u) ---\n", i->tablename, i->set); + printf(" kindex: %d, type: %s\n", i->kidx, ttype); printf(" valtype: %s, references: %u\n", vtype, i->refcnt); printf(" algorithm: %s\n", i->algoname); printf(" items: %u, size: %u\n", i->count, i->size); @@ -575,12 +640,15 @@ table_lookup(ipfw_obj_header *oh, int ac { ipfw_obj_tentry xtent; ipfw_xtable_info xi; + char key[64]; int error; if (ac == 0) errx(EX_USAGE, "address required"); - error = table_do_lookup(oh, *av, &xi, &xtent); + strlcpy(key, *av, sizeof(key)); + + error = table_do_lookup(oh, key, &xi, &xtent); switch (error) { case 0: @@ -600,12 +668,17 @@ table_lookup(ipfw_obj_header *oh, int ac } static void -tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type) +tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type, + uint8_t tflags) { - char *p; + char *p, *pp; int mask, af; - struct in6_addr *paddr; + struct in6_addr *paddr, tmp; + struct tflow_entry *tfe; uint32_t key, *pkey; + uint16_t port; + struct protoent *pent; + struct servent *sent; int masklen; masklen = 0; @@ -664,6 +737,117 @@ tentry_fill_key_type(char *arg, ipfw_obj *pkey = key; masklen = 32; break; + case IPFW_TABLE_FLOW: + /* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */ + tfe = &tentry->k.flow; + af = 0; + + /* Handle <ipv4|ipv6>*/ + if ((tflags & IPFW_TFFLAG_SRCIP) != 0) { + if ((p = strchr(arg, ',')) != NULL) + *p++ = '\0'; + /* Determine family using temporary storage */ + if (inet_pton(AF_INET, arg, &tmp) == 1) { + if (af != 0 && af != AF_INET) + errx(EX_DATAERR, + "Inconsistent address family\n"); + af = AF_INET; + memcpy(&tfe->a.a4.sip, &tmp, 4); + } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { + if (af != 0 && af != AF_INET6) + errx(EX_DATAERR, + "Inconsistent address family\n"); + af = AF_INET6; + memcpy(&tfe->a.a6.sip6, &tmp, 16); + } + + arg = p; + } + + /* Handle <proto-num|proto-name> */ + if ((tflags & IPFW_TFFLAG_PROTO) != 0) { + if ((p = strchr(arg, ',')) != NULL) + *p++ = '\0'; + + key = strtol(arg, &pp, 10); + if (*pp != '\0') { + if ((pent = getprotobyname(arg)) == NULL) + errx(EX_DATAERR, "Unknown proto: %s", + arg); + else + key = pent->p_proto; + } + + if (key > 255) + errx(EX_DATAERR, "Bad protocol number: %u",key); + + tfe->proto = key; + + arg = p; + } + + /* Handle <port-num|service-name> */ + if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) { + if ((p = strchr(arg, ',')) != NULL) + *p++ = '\0'; + + if ((port = htons(strtol(arg, NULL, 10))) == 0) { + if ((sent = getservbyname(arg, NULL)) == NULL) + errx(EX_DATAERR, "Unknown service: %s", + arg); + else + key = sent->s_port; + } + + tfe->sport = port; + + arg = p; + } + + /* Handle <ipv4|ipv6>*/ + if ((tflags & IPFW_TFFLAG_DSTIP) != 0) { + if ((p = strchr(arg, ',')) != NULL) + *p++ = '\0'; + /* Determine family using temporary storage */ + if (inet_pton(AF_INET, arg, &tmp) == 1) { + if (af != 0 && af != AF_INET) + errx(EX_DATAERR, + "Inconsistent address family"); + af = AF_INET; + memcpy(&tfe->a.a4.dip, &tmp, 4); + } else if (inet_pton(AF_INET6, arg, &tmp) == 1) { + if (af != 0 && af != AF_INET6) + errx(EX_DATAERR, + "Inconsistent address family"); + af = AF_INET6; + memcpy(&tfe->a.a6.dip6, &tmp, 16); + } + + arg = p; + } + + /* Handle <port-num|service-name> */ + if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) { + if ((p = strchr(arg, ',')) != NULL) + *p++ = '\0'; + + if ((port = htons(strtol(arg, NULL, 10))) == 0) { + if ((sent = getservbyname(arg, NULL)) == NULL) + errx(EX_DATAERR, "Unknown service: %s", + arg); + else + key = sent->s_port; + } + + tfe->dport = port; + + arg = p; + } + + tfe->af = af; + + break; + default: errx(EX_DATAERR, "Unsupported table type: %d", type); } @@ -676,11 +860,12 @@ 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; + uint8_t type, tflags, vtype; int error; char *del; type = 0; + tflags = 0; vtype = 0; error = table_get_info(oh, xi); @@ -688,6 +873,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipf if (error == 0) { /* Table found. */ type = xi->type; + tflags = xi->tflags; vtype = xi->vtype; } else { if (error != ESRCH) @@ -718,7 +904,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipf *del = '/'; } - tentry_fill_key_type(key, tent, type); + tentry_fill_key_type(key, tent, type, tflags); *ptype = type; *pvtype = vtype; @@ -874,41 +1060,75 @@ table_show_list(ipfw_obj_header *oh, int static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent) { - char tbuf[128]; + char *comma, tbuf[128], pval[32]; + void *paddr; uint32_t tval; + struct tflow_entry *tfe; tval = tent->value; + if (co.do_value_as_ip) { + tval = htonl(tval); + inet_ntop(AF_INET, &tval, pval, sizeof(pval)); + } else + snprintf(pval, sizeof(pval), "%u", tval); + switch (i->type) { case IPFW_TABLE_CIDR: /* IPv4 or IPv6 prefixes */ inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf)); - - if (co.do_value_as_ip) { - tval = htonl(tval); - printf("%s/%u %s\n", tbuf, tent->masklen, - inet_ntoa(*(struct in_addr *)&tval)); - } else - printf("%s/%u %u\n", tbuf, tent->masklen, tval); + printf("%s/%u %s\n", tbuf, tent->masklen, pval); break; case IPFW_TABLE_INTERFACE: /* Interface names */ - if (co.do_value_as_ip) { - tval = htonl(tval); - printf("%s %s\n", tent->k.iface, - inet_ntoa(*(struct in_addr *)&tval)); - } else - printf("%s %u\n", tent->k.iface, tval); + printf("%s %s\n", tent->k.iface, pval); break; case IPFW_TABLE_NUMBER: /* numbers */ - if (co.do_value_as_ip) { - tval = htonl(tval); - printf("%u %s\n", tent->k.key, - inet_ntoa(*(struct in_addr *)&tval)); - } else - printf("%u %u\n", tent->k.key, tval); + printf("%u %s\n", tent->k.key, pval); break; + case IPFW_TABLE_FLOW: + /* flows */ + tfe = &tent->k.flow; + comma = ""; + + if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) { + if (tfe->af == AF_INET) + paddr = &tfe->a.a4.sip; + else + paddr = &tfe->a.a6.sip6; + + inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); + printf("%s%s", comma, tbuf); + comma = ","; + } + + if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) { + printf("%s%d", comma, tfe->proto); + comma = ","; + } + + if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) { + printf("%s%d", comma, ntohs(tfe->sport)); + comma = ","; + } + if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) { + if (tfe->af == AF_INET) + paddr = &tfe->a.a4.dip; + else + paddr = &tfe->a.a6.dip6; + + inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf)); + printf("%s%s", comma, tbuf); + comma = ","; + } + + if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) { + printf("%s%d", comma, ntohs(tfe->dport)); + comma = ","; + } + + printf(" %s\n", pval); } } Modified: projects/ipfw/sys/netinet/ip_fw.h ============================================================================== --- projects/ipfw/sys/netinet/ip_fw.h Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sys/netinet/ip_fw.h Thu Jul 31 20:08:19 2014 (r269348) @@ -262,6 +262,7 @@ enum ipfw_opcodes { /* arguments (4 byt O_DSCP, /* 2 u32 = DSCP mask */ O_SETDSCP, /* arg1=DSCP value */ + O_IP_FLOW_LOOKUP, /* arg1=table number, u32=value */ O_LAST_OPCODE /* not an opcode! */ }; @@ -675,7 +676,8 @@ 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_NUMBER 3 /* Table for holding ports/uid/gid/etc */ -#define IPFW_TABLE_MAXTYPE 3 /* Maximum valid number */ +#define IPFW_TABLE_FLOW 4 /* Table for holding flow data */ +#define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */ #define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */ #define IPFW_VTYPE_IP 2 /* Nexthop IP address */ @@ -743,6 +745,25 @@ typedef struct _ipfw_obj_ntlv { char name[64]; /* Null-terminated name */ } ipfw_obj_ntlv; +/* IPv4/IPv6 L4 flow description */ +struct tflow_entry { + uint8_t af; + uint8_t proto; + uint16_t spare; + uint16_t sport; + uint16_t dport; + union { + struct { + struct in_addr sip; + struct in_addr dip; + } a4; + struct { + struct in6_addr sip6; + struct in6_addr dip6; + } a6; + } a; +}; + /* Table entry TLV */ typedef struct _ipfw_obj_tentry { ipfw_obj_tlv head; /* TLV header */ @@ -753,10 +774,11 @@ typedef struct _ipfw_obj_tentry { uint64_t spare; 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 */ + struct in_addr addr; /* IPv4 address */ + uint32_t key; /* uid/gid/port */ + struct in6_addr addr6; /* IPv6 address */ char iface[IF_NAMESIZE]; /* interface name */ + struct tflow_entry flow; } k; } ipfw_obj_tentry; #define IPFW_TF_UPDATE 0x01 /* Update record if exists */ @@ -776,19 +798,44 @@ typedef struct _ipfw_obj_ctlv { uint8_t spare; } ipfw_obj_ctlv; +typedef struct _ifpw_ta_tinfo { + uint32_t flags; /* Format flags */ + uint8_t taclass; /* algorithm class */ + uint8_t spare0; + uint16_t spare1; + uint32_t rssize4; /* runtime structure size */ + uint32_t rcount4; /* number of items in runtime */ + uint32_t rsize4; /* item size in runtime */ + uint32_t rssize6; /* runtime structure size */ + uint32_t rcount6; /* number of items in runtime */ + uint32_t rsize6; /* item size in runtime */ +} ifpw_ta_tinfo; +#define IPFW_TACLASS_HASH 1 /* algo is based on hash */ +#define IPFW_TACLASS_ARRAY 2 /* algo is based on array */ +#define IPFW_TACLASS_RADIX 3 /* algo is based on radix tree */ + +#define IPFW_TATFLAGS_DATA 0x0001 /* Has data filled in */ +#define IPFW_TATFLAGS_AF 0x0002 /* Separate data per AF */ + typedef struct _ipfw_xtable_info { uint8_t type; /* table type (cidr,iface,..) */ + uint8_t tflags; /* type flags */ uint8_t ftype; /* table value format type */ 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 */ uint32_t count; /* Number of records */ - uint32_t size; /* Total size of records */ + uint32_t size; /* Total size of records(export)*/ char tablename[64]; /* table name */ - char algoname[32]; /* algorithm name */ + char algoname[64]; /* algorithm name */ + ifpw_ta_tinfo ta_info; /* additional algo stats */ } ipfw_xtable_info; +#define IPFW_TFFLAG_SRCIP 0x01 +#define IPFW_TFFLAG_DSTIP 0x02 +#define IPFW_TFFLAG_SRCPORT 0x04 +#define IPFW_TFFLAG_DSTPORT 0x08 +#define IPFW_TFFLAG_PROTO 0x10 typedef struct _ipfw_iface_info { char ifname[64]; /* interface name */ @@ -801,7 +848,7 @@ typedef struct _ipfw_iface_info { #define IPFW_IFFLAG_RESOLVED 0x01 /* Interface exists */ typedef struct _ipfw_ta_info { - char algoname[32]; /* algorithm name */ + char algoname[64]; /* algorithm name */ uint32_t type; /* lookup type */ uint32_t flags; uint32_t refcnt; Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c Thu Jul 31 20:08:19 2014 (r269348) @@ -1522,6 +1522,17 @@ do { \ } break; + case O_IP_FLOW_LOOKUP: + { + uint32_t v = 0; + match = ipfw_lookup_table_extended(chain, + cmd->arg1, 0, &args->f_id, &v); + if (cmdlen == F_INSN_SIZE(ipfw_insn_u32)) + match = ((ipfw_insn_u32 *)cmd)->d[0] == v; + if (match) + tablearg = v; + } + break; case O_IP_SRC_MASK: case O_IP_DST_MASK: if (is_ipv4) { Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Thu Jul 31 20:08:19 2014 (r269348) @@ -1011,6 +1011,17 @@ check_ipfw_rule_body(ipfw_insn *cmd, int goto bad_size; ci->table_opcodes++; break; + case O_IP_FLOW_LOOKUP: + if (cmd->arg1 >= V_fw_tables_max) { + printf("ipfw: invalid table number %d\n", + cmd->arg1); + return (EINVAL); + } + if (cmdlen != F_INSN_SIZE(ipfw_insn) && + cmdlen != F_INSN_SIZE(ipfw_insn_u32)) + goto bad_size; + ci->table_opcodes++; + break; case O_MACADDR2: if (cmdlen != F_INSN_SIZE(ipfw_insn_mac)) goto bad_size; @@ -1726,7 +1737,7 @@ ipfw_ctl3(struct sockopt *sopt) size_t bsize_max, size, valsize; struct ip_fw_chain *chain; uint32_t opt; - char xbuf[128]; + char xbuf[256]; struct sockopt_data sdata; ip_fw3_opheader *op3 = NULL; Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c ============================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Thu Jul 31 19:24:44 2014 (r269347) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Thu Jul 31 20:08:19 2014 (r269348) @@ -77,7 +77,8 @@ struct table_config { struct named_object no; uint8_t vtype; /* format table type */ uint8_t linked; /* 1 if already linked */ - uint16_t spare; + uint8_t tflags; /* type flags */ + uint8_t spare; uint32_t count; /* Number of records */ uint64_t flags; /* state flags */ char tablename[64]; /* table name */ @@ -95,11 +96,12 @@ struct tables_config { static struct table_config *find_table(struct namedobj_instance *ni, struct tid_info *ti); static struct table_config *alloc_table_config(struct ip_fw_chain *ch, - struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t vtype); + struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags, + uint8_t vtype); static void free_table_config(struct namedobj_instance *ni, struct table_config *tc); static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, uint8_t vtype); + char *aname, uint8_t tflags, uint8_t vtype); static void link_table(struct ip_fw_chain *chain, struct table_config *tc); 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); @@ -169,7 +171,7 @@ add_table_entry(struct ip_fw_chain *ch, if ((tei->flags & TEI_FLAGS_COMPAT) == 0) return (ESRCH); - error = create_table_internal(ch, ti, NULL, IPFW_VTYPE_U32); + error = create_table_internal(ch, ti, NULL, 0, IPFW_VTYPE_U32); if (error != 0) return (error); @@ -533,8 +535,7 @@ ipfw_find_table_entry(struct ip_fw_chain struct table_algo *ta; struct table_info *kti; struct namedobj_instance *ni; - int error, plen; - void *paddr; + int error; size_t sz; /* Check minimum header size */ @@ -571,41 +572,13 @@ ipfw_find_table_entry(struct ip_fw_chain return (EINVAL); } - /* Check lookup key for validness */ - plen = 0; - paddr = &tent->k; - switch (ti.type) - { - case IPFW_TABLE_CIDR: - if (tent->subtype == AF_INET) - plen = sizeof(struct in_addr); - else if (tent->subtype == AF_INET6) - plen = sizeof(struct in6_addr); - else { - IPFW_UH_RUNLOCK(ch); - return (EINVAL); - } - break; - case IPFW_TABLE_INTERFACE: - /* Check key first */ - plen = sizeof(tent->k.iface); - if (strnlen(tent->k.iface, plen) == plen) { - IPFW_UH_RUNLOCK(ch); - return (EINVAL); - } - case IPFW_TABLE_NUMBER: - plen = sizeof(uint32_t); - break; - - break; - default: - IPFW_UH_RUNLOCK(ch); - return (ENOTSUP); - } kti = KIDX_TO_TI(ch, tc->no.kidx); ta = tc->ta; - error = ta->find_tentry(tc->astate, kti, paddr, plen, tent); + if (ta->find_tentry == NULL) + return (ENOTSUP); + + error = ta->find_tentry(tc->astate, kti, tent); IPFW_UH_RUNLOCK(ch); @@ -651,9 +624,10 @@ flush_table(struct ip_fw_chain *ch, stru struct table_algo *ta; struct table_info ti_old, ti_new, *tablestate; void *astate_old, *astate_new; - char algostate[32], *pstate; + char algostate[64], *pstate; int error; uint16_t kidx; + uint8_t tflags; /* * Stage 1: save table algoritm. @@ -674,13 +648,14 @@ flush_table(struct ip_fw_chain *ch, stru pstate = algostate; } else pstate = NULL; + tflags = tc->tflags; IPFW_UH_WUNLOCK(ch); /* * Stage 2: allocate new table instance using same algo. */ memset(&ti_new, 0, sizeof(struct table_info)); - if ((error = ta->init(ch, &astate_new, &ti_new, pstate)) != 0) { + if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) { IPFW_UH_WLOCK(ch); tc->no.refcnt--; IPFW_UH_WUNLOCK(ch); @@ -1211,7 +1186,7 @@ ipfw_create_table(struct ip_fw_chain *ch } IPFW_UH_RUNLOCK(ch); - return (create_table_internal(ch, &ti, aname, i->vtype)); + return (create_table_internal(ch, &ti, aname, i->tflags, i->vtype)); } /* @@ -1224,7 +1199,7 @@ ipfw_create_table(struct ip_fw_chain *ch */ static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti, - char *aname, uint8_t vtype) + char *aname, uint8_t tflags, uint8_t vtype) { struct namedobj_instance *ni; struct table_config *tc; @@ -1237,7 +1212,7 @@ create_table_internal(struct ip_fw_chain if (ta == NULL) return (ENOTSUP); - if ((tc = alloc_table_config(ch, ti, ta, aname, vtype)) == NULL) + if ((tc = alloc_table_config(ch, ti, ta, aname, tflags, vtype)) == NULL) return (ENOMEM); IPFW_UH_WLOCK(ch); @@ -1311,6 +1286,7 @@ export_table_info(struct ip_fw_chain *ch struct table_info *ti; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201407312008.s6VK8J9R083960>