ipfw lookup dst-port rules don't seem to work. Didn't test similar cases, like src-port. Fix: I worked around it for the moment by writing the rule without a lookup table; don't have time to kernel spelunk. How-To-Repeat: Load these ipfw rules: table 1 add 22 add 00001 permit log ip4 from any to any proto tcp lookup dst-port 1 add 00010 permit log ip from any to any proto tcp dst-port 22 Observe how on freebsd 9.1, rule 1 will never match port 22 traffic it should, whereas the same rules on 8.3 will hit rule 1, as expected.
Daniel, Can you please try whether the problem can be reproduced on stable/9 branch prior to revision r234597? To accomplish this you need: # svn co -r r234596 http://svn.freebsd.org/base/stable/9 # cd 9 # make buildworld buildkernel # make installkernel installworld reboot -- Totus tuus, Glebius.
Responsible Changed From-To: freebsd-bugs->melifaro I can reproduce this issue on my pre-9.1 desktop. There is no need for doing kernel recompiling.
> Can you please try whether the problem can be reproduced on stable/9 > branch prior to revision r234597? Your hunch is confirmed. 234597 demonstrates the bug, 234593 (the preceding commit on stable/9) does not. 234593: 00001 7 388 allow log ip4 from any to any proto tcp lookup dst-port 1 00010 0 0 allow log ip4 from any to any proto tcp dst-port 29 vs 234597: 00001 0 0 allow log ip4 from any to any proto tcp lookup dst-port 1 00010 6 336 allow log ip4 from any to any proto tcp dst-port 29
Author: melifaro Date: Sat Mar 2 18:51:26 2013 New Revision: 247666 URL: http://svnweb.freebsd.org/changeset/base/247666 Log: Fix ipfw table argument parsing/printing. Fix style. PR: kern/175909 Submitted by: Daniel Hagerty <hag@linnaean.org> MFC after: 2 weeks Modified: head/sbin/ipfw/ipfw2.c Modified: head/sbin/ipfw/ipfw2.c ============================================================================== --- head/sbin/ipfw/ipfw2.c Sat Mar 2 18:40:04 2013 (r247665) +++ head/sbin/ipfw/ipfw2.c Sat Mar 2 18:51:26 2013 (r247666) @@ -3912,6 +3912,7 @@ ipfw_flush(int force) static void table_list(uint16_t num, int need_header); +static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); /* * This one handles all table-related commands @@ -3927,8 +3928,7 @@ ipfw_table_handler(int ac, char *av[]) int do_add; int is_all; size_t len; - char *p; - uint32_t a, type, mask, addrlen; + uint32_t a, mask; uint32_t tables_max; mask = 0; // XXX uninitialized ? @@ -3965,57 +3965,8 @@ ipfw_table_handler(int ac, char *av[]) ac--; av++; if (!ac) errx(EX_USAGE, "address required"); - /* - * Let's try to guess type by agrument. - * Possible types: - * 1) IPv4[/mask] - * 2) IPv6[/mask] - * 3) interface name - * 4) port ? - */ - type = 0; - if (ishexnumber(*av[0])) { - /* Remove / if exists */ - if ((p = strchr(*av, '/')) != NULL) { - *p = '\0'; - mask = atoi(p + 1); - } - - if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) { - type = IPFW_TABLE_CIDR; - if ((p != NULL) && (mask > 32)) - errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); - xent.masklen = p ? mask : 32; - addrlen = sizeof(struct in_addr); - } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 1) { - type = IPFW_TABLE_CIDR; - if ((p != NULL) && (mask > 128)) - errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); - xent.masklen = p ? mask : 128; - addrlen = sizeof(struct in6_addr); - } - } - - if ((type == 0) && (strchr(*av, '.') == NULL)) { - /* Assume interface name. Copy significant data only */ - mask = MIN(strlen(*av), IF_NAMESIZE - 1); - memcpy(xent.k.iface, *av, mask); - /* Set mask to exact match */ - xent.masklen = 8 * IF_NAMESIZE; - type = IPFW_TABLE_INTERFACE; - addrlen = IF_NAMESIZE; - } - if (type == 0) { - if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) != 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); - xent.masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); - } - - xent.type = type; - xent.len = offsetof(ipfw_table_xentry, k) + addrlen; + table_fill_xentry(*av, &xent); ac--; av++; if (do_add && ac) { @@ -4065,6 +4016,93 @@ ipfw_table_handler(int ac, char *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); + } else { + /* Port or any other key */ + key = strtol(arg, &p, 10); + /* Skip non-base 10 entries like 'fa1' */ + if (p != arg) { + pkey = (uint32_t *)paddr; + *pkey = htonl(key); + type = IPFW_TABLE_CIDR; + addrlen = sizeof(uint32_t); + } + } + } + + 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_list(uint16_t num, int need_header) { ipfw_xtable *tbl; @@ -4107,8 +4145,8 @@ table_list(uint16_t num, int need_header tval = xent->value; addr6 = &xent->k.addr6; - if ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] == 0) && - (addr6->s6_addr32[2] == 0)) { + + if (IN6_IS_ADDR_V4COMPAT(addr6)) { /* IPv4 address */ inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); } else { _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
State Changed From-To: open->patched Fixed in HEAD.
Author: melifaro Date: Tue Mar 19 13:29:01 2013 New Revision: 248505 URL: http://svnweb.freebsd.org/changeset/base/248505 Log: Merge r247666, r247712, r247811. Fix ipfw table argument parsing/printing. Fix style. PR: kern/175909 Submitted by: Daniel Hagerty <hag@linnaean.org> Implement buffer size checking in ipfw(8) add cmd. PR: bin/65961 Submitted by: Eugene Grosbein <eugen@grosbein.pp.ru> Do not suddenly fail on some rulesets if -n (syntax check only) is specified and ipfw(4) module is not loaded. Modified: stable/9/sbin/ipfw/ipfw2.c stable/9/sbin/ipfw/ipfw2.h stable/9/sbin/ipfw/ipv6.c Directory Properties: stable/9/sbin/ (props changed) stable/9/sbin/ipfw/ (props changed) Modified: stable/9/sbin/ipfw/ipfw2.c ============================================================================== --- stable/9/sbin/ipfw/ipfw2.c Tue Mar 19 13:21:39 2013 (r248504) +++ stable/9/sbin/ipfw/ipfw2.c Tue Mar 19 13:29:01 2013 (r248505) @@ -64,6 +64,22 @@ int ipfw_socket = -1; #define s6_addr32 __u6_addr.__u6_addr32 #endif +#define CHECK_LENGTH(v, len) do { \ + if ((v) < (len)) \ + errx(EX_DATAERR, "Rule too long"); \ + } while (0) +/* + * Check if we have enough space in cmd buffer. Note that since + * first 8? u32 words are reserved by reserved header, full cmd + * buffer can't be used, so we need to protect from buffer overrun + * only. At the beginnig, cblen is less than actual buffer size by + * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need + * for checking small instructions fitting in given range. + * We also (ab)use the fact that ipfw_insn is always the first field + * for any custom instruction. + */ +#define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd)) + #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ @@ -653,7 +669,7 @@ strtoport(char *s, char **end, int base, * Fill the body of the command with the list of port ranges. */ static int -fill_newports(ipfw_insn_u16 *cmd, char *av, int proto) +fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen) { uint16_t a, b, *p = cmd->ports; int i = 0; @@ -664,6 +680,8 @@ fill_newports(ipfw_insn_u16 *cmd, char * if (s == av) /* empty or invalid argument */ return (0); + CHECK_LENGTH(cblen, i + 2); + switch (*s) { case '-': /* a range */ av = s + 1; @@ -2067,7 +2085,7 @@ lookup_host (char *host, struct in_addr * We can have multiple comma-separated address/mask entries. */ static void -fill_ip(ipfw_insn_ip *cmd, char *av) +fill_ip(ipfw_insn_ip *cmd, char *av, int cblen) { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; @@ -2107,6 +2125,8 @@ fill_ip(ipfw_insn_ip *cmd, char *av) int masklen; char md, nd = '\0'; + CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len); + if (p) { md = *p; *p++ = '\0'; @@ -2365,11 +2385,13 @@ ipfw_delete(char *av[]) * patterns which match interfaces. */ static void -fill_iface(ipfw_insn_if *cmd, char *arg) +fill_iface(ipfw_insn_if *cmd, char *arg, int cblen) { cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); + CHECK_CMDLEN; + /* Parse the interface or address */ if (strcmp(arg, "any") == 0) cmd->o.len = 0; /* effectively ignore this command */ @@ -2440,8 +2462,10 @@ get_mac_addr_mask(const char *p, uint8_t * the new command in case it has been clobbered before. */ static ipfw_insn * -next_cmd(ipfw_insn *cmd) +next_cmd(ipfw_insn *cmd, int *len) { + *len -= F_LEN(cmd); + CHECK_LENGTH(*len, 0); cmd += F_LEN(cmd); bzero(cmd, sizeof(*cmd)); return cmd; @@ -2451,7 +2475,7 @@ next_cmd(ipfw_insn *cmd) * Takes arguments and copies them into a comment */ static void -fill_comment(ipfw_insn *cmd, char **av) +fill_comment(ipfw_insn *cmd, char **av, int cblen) { int i, l; char *p = (char *)(cmd + 1); @@ -2469,6 +2493,8 @@ fill_comment(ipfw_insn *cmd, char **av) "comment too long (max 80 chars)"); l = 1 + (l+3)/4; cmd->len = (cmd->len & (F_NOT | F_OR)) | l; + CHECK_CMDLEN; + for (i = 0; av[i] != NULL; i++) { strcpy(p, av[i]); p += strlen(av[i]); @@ -2494,7 +2520,7 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcod * two microinstructions, and returns the pointer to the last one. */ static ipfw_insn * -add_mac(ipfw_insn *cmd, char *av[]) +add_mac(ipfw_insn *cmd, char *av[], int cblen) { ipfw_insn_mac *mac; @@ -2503,6 +2529,7 @@ add_mac(ipfw_insn *cmd, char *av[]) cmd->opcode = O_MACADDR2; cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); + CHECK_CMDLEN; mac = (ipfw_insn_mac *)cmd; get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ @@ -2512,12 +2539,13 @@ add_mac(ipfw_insn *cmd, char *av[]) } static ipfw_insn * -add_mactype(ipfw_insn *cmd, char *av) +add_mactype(ipfw_insn *cmd, char *av, int cblen) { if (!av) errx(EX_DATAERR, "missing MAC type"); if (strcmp(av, "any") != 0) { /* we have a non-null type */ - fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE); + fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE, + cblen); cmd->opcode = O_MAC_TYPE; return cmd; } else @@ -2586,9 +2614,9 @@ add_proto_compat(ipfw_insn *cmd, char *a } static ipfw_insn * -add_srcip(ipfw_insn *cmd, char *av) +add_srcip(ipfw_insn *cmd, char *av, int cblen) { - fill_ip((ipfw_insn_ip *)cmd, av); + fill_ip((ipfw_insn_ip *)cmd, av, cblen); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ @@ -2603,9 +2631,9 @@ add_srcip(ipfw_insn *cmd, char *av) } static ipfw_insn * -add_dstip(ipfw_insn *cmd, char *av) +add_dstip(ipfw_insn *cmd, char *av, int cblen) { - fill_ip((ipfw_insn_ip *)cmd, av); + fill_ip((ipfw_insn_ip *)cmd, av, cblen); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ @@ -2620,12 +2648,12 @@ add_dstip(ipfw_insn *cmd, char *av) } static ipfw_insn * -add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) +add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) { /* XXX "any" is trapped before. Perhaps "to" */ if (_substrcmp(av, "any") == 0) { return NULL; - } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) { + } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { /* XXX todo: check that we have a protocol with ports */ cmd->opcode = opcode; return cmd; @@ -2634,7 +2662,7 @@ add_ports(ipfw_insn *cmd, char *av, u_ch } static ipfw_insn * -add_src(ipfw_insn *cmd, char *av, u_char proto) +add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen) { struct in6_addr a; char *host, *ch; @@ -2647,11 +2675,11 @@ add_src(ipfw_insn *cmd, char *av, u_char if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) - ret = add_srcip6(cmd, av); + ret = add_srcip6(cmd, av, cblen); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) - ret = add_srcip(cmd, av); + ret = add_srcip(cmd, av, cblen); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; @@ -2660,7 +2688,7 @@ add_src(ipfw_insn *cmd, char *av, u_char } static ipfw_insn * -add_dst(ipfw_insn *cmd, char *av, u_char proto) +add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen) { struct in6_addr a; char *host, *ch; @@ -2673,11 +2701,11 @@ add_dst(ipfw_insn *cmd, char *av, u_char if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || inet_pton(AF_INET6, host, &a) == 1) - ret = add_dstip6(cmd, av); + ret = add_dstip6(cmd, av, cblen); /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) - ret = add_dstip(cmd, av); + ret = add_dstip(cmd, av, cblen); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; @@ -2707,6 +2735,7 @@ ipfw_add(char *av[]) * go into actbuf[]. */ static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; + int rblen, ablen, cblen; ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ @@ -2737,6 +2766,15 @@ ipfw_add(char *av[]) cmd = (ipfw_insn *)cmdbuf; action = (ipfw_insn *)actbuf; + rblen = sizeof(rulebuf) / sizeof(rulebuf[0]); + rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]); + ablen = sizeof(actbuf) / sizeof(actbuf[0]); + cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]); + cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; + +#define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; } +#define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len) + av++; /* [rule N] -- Rule number optional */ @@ -2768,6 +2806,7 @@ ipfw_add(char *av[]) i = match_token(rule_actions, *av); av++; action->len = 1; /* default */ + CHECK_ACTLEN; switch(i) { case TOK_CHECKSTATE: have_state = action; @@ -2819,6 +2858,7 @@ ipfw_add(char *av[]) case TOK_NAT: action->opcode = O_NAT; action->len = F_INSN_SIZE(ipfw_insn_nat); + CHECK_ACTLEN; if (_substrcmp(*av, "global") == 0) { action->arg1 = 0; av++; @@ -2935,6 +2975,7 @@ chkarg: action->opcode = O_FORWARD_IP; action->len = F_INSN_SIZE(ipfw_insn_sa); + CHECK_ACTLEN; /* * In the kernel we assume AF_INET and use only @@ -2951,6 +2992,7 @@ chkarg: action->opcode = O_FORWARD_IP6; action->len = F_INSN_SIZE(ipfw_insn_sa6); + CHECK_ACTLEN; p->sa.sin6_len = sizeof(struct sockaddr_in6); p->sa.sin6_family = AF_INET6; @@ -3004,7 +3046,7 @@ chkarg: default: errx(EX_DATAERR, "invalid action %s\n", av[-1]); } - action = next_cmd(action); + action = next_cmd(action, &ablen); /* * [altq queuename] -- altq tag, optional @@ -3026,6 +3068,7 @@ chkarg: "log cannot be specified more than once"); have_log = (ipfw_insn *)c; cmd->len = F_INSN_SIZE(ipfw_insn_log); + CHECK_CMDLEN; cmd->opcode = O_LOG; if (av[0] && _substrcmp(*av, "logamount") == 0) { av++; @@ -3039,9 +3082,14 @@ chkarg: } else { len = sizeof(c->max_log); if (sysctlbyname("net.inet.ip.fw.verbose_limit", - &c->max_log, &len, NULL, 0) == -1) + &c->max_log, &len, NULL, 0) == -1) { + if (co.test_only) { + c->max_log = 0; + break; + } errx(1, "sysctlbyname(\"%s\")", "net.inet.ip.fw.verbose_limit"); + } } } break; @@ -3057,6 +3105,7 @@ chkarg: "altq cannot be specified more than once"); have_altq = (ipfw_insn *)a; cmd->len = F_INSN_SIZE(ipfw_insn_altq); + CHECK_CMDLEN; cmd->opcode = O_ALTQ; a->qid = altq_name_to_qid(*av); av++; @@ -3082,7 +3131,7 @@ chkarg: default: abort(); } - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } if (have_state) /* must be a check-state, we are done */ @@ -3167,7 +3216,7 @@ chkarg: av++; if (F_LEN(cmd) != 0) { prev = cmd; - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } else if (first_cmd != cmd) { errx(EX_DATAERR, "invalid protocol ``%s''", *av); @@ -3188,11 +3237,11 @@ chkarg: OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); - if (add_src(cmd, *av, proto)) { + if (add_src(cmd, *av, proto, cblen)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } else errx(EX_USAGE, "bad source address %s", *av); @@ -3204,10 +3253,10 @@ chkarg: NOT_BLOCK; /* optional "not" */ if ( av[0] != NULL ) { if (_substrcmp(*av, "any") == 0 || - add_ports(cmd, *av, proto, O_IP_SRCPORT)) { + add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; if (F_LEN(cmd) != 0) - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } @@ -3224,11 +3273,11 @@ chkarg: OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); - if (add_dst(cmd, *av, proto)) { + if (add_dst(cmd, *av, proto, cblen)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } else errx( EX_USAGE, "bad destination address %s", *av); @@ -3240,10 +3289,10 @@ chkarg: NOT_BLOCK; /* optional "not" */ if (av[0]) { if (_substrcmp(*av, "any") == 0 || - add_ports(cmd, *av, proto, O_IP_DSTPORT)) { + add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; if (F_LEN(cmd) != 0) - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } @@ -3331,7 +3380,7 @@ read_options: case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); - fill_iface((ipfw_insn_if *)cmd, av[0]); + fill_iface((ipfw_insn_if *)cmd, av[0], cblen); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; @@ -3351,14 +3400,14 @@ read_options: case TOK_ICMP6TYPES: NEED1("icmptypes requires list of types"); - fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av); + fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen); av++; break; case TOK_IPTTL: NEED1("ipttl requires TTL"); if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_IPTTL)) + if (!add_ports(cmd, *av, 0, O_IPTTL, cblen)) errx(EX_DATAERR, "invalid ipttl %s", *av); } else fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); @@ -3368,7 +3417,7 @@ read_options: case TOK_IPID: NEED1("ipid requires id"); if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_IPID)) + if (!add_ports(cmd, *av, 0, O_IPID, cblen)) errx(EX_DATAERR, "invalid ipid %s", *av); } else fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); @@ -3378,7 +3427,7 @@ read_options: case TOK_IPLEN: NEED1("iplen requires length"); if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_IPLEN)) + if (!add_ports(cmd, *av, 0, O_IPLEN, cblen)) errx(EX_DATAERR, "invalid ip len %s", *av); } else fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); @@ -3474,7 +3523,7 @@ read_options: case TOK_TCPDATALEN: NEED1("tcpdatalen requires length"); if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_TCPDATALEN)) + if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen)) errx(EX_DATAERR, "invalid tcpdata len %s", *av); } else fill_cmd(cmd, O_TCPDATALEN, 0, @@ -3500,7 +3549,7 @@ read_options: case TOK_TCPWIN: NEED1("tcpwin requires length"); if (strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_TCPWIN)) + if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen)) errx(EX_DATAERR, "invalid tcpwin len %s", *av); } else fill_cmd(cmd, O_TCPWIN, 0, @@ -3539,6 +3588,7 @@ read_options: have_state = cmd; cmd->len = F_INSN_SIZE(ipfw_insn_limit); + CHECK_CMDLEN; cmd->opcode = O_LIMIT; c->limit_mask = c->conn_limit = 0; @@ -3570,28 +3620,28 @@ read_options: case TOK_SRCIP: NEED1("missing source IP"); - if (add_srcip(cmd, *av)) { + if (add_srcip(cmd, *av, cblen)) { av++; } break; case TOK_DSTIP: NEED1("missing destination IP"); - if (add_dstip(cmd, *av)) { + if (add_dstip(cmd, *av, cblen)) { av++; } break; case TOK_SRCIP6: NEED1("missing source IP6"); - if (add_srcip6(cmd, *av)) { + if (add_srcip6(cmd, *av, cblen)) { av++; } break; case TOK_DSTIP6: NEED1("missing destination IP6"); - if (add_dstip6(cmd, *av)) { + if (add_dstip6(cmd, *av, cblen)) { av++; } break; @@ -3599,7 +3649,7 @@ read_options: case TOK_SRCPORT: NEED1("missing source port"); if (_substrcmp(*av, "any") == 0 || - add_ports(cmd, *av, proto, O_IP_SRCPORT)) { + add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid source port %s", *av); @@ -3608,7 +3658,7 @@ read_options: case TOK_DSTPORT: NEED1("missing destination port"); if (_substrcmp(*av, "any") == 0 || - add_ports(cmd, *av, proto, O_IP_DSTPORT)) { + add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { av++; } else errx(EX_DATAERR, "invalid destination port %s", @@ -3616,13 +3666,13 @@ read_options: break; case TOK_MAC: - if (add_mac(cmd, av)) + if (add_mac(cmd, av, cblen)) av += 2; break; case TOK_MACTYPE: NEED1("missing mac type"); - if (!add_mactype(cmd, *av)) + if (!add_mactype(cmd, *av, cblen)) errx(EX_DATAERR, "invalid mac type %s", *av); av++; break; @@ -3660,18 +3710,18 @@ read_options: if (proto != IPPROTO_IPV6 ) errx( EX_USAGE, "flow-id filter is active " "only for ipv6 protocol\n"); - fill_flow6( (ipfw_insn_u32 *) cmd, *av ); + fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen); av++; break; case TOK_COMMENT: - fill_comment(cmd, av); + fill_comment(cmd, av, cblen); av[0]=NULL; break; case TOK_TAGGED: if (av[0] && strpbrk(*av, "-,")) { - if (!add_ports(cmd, *av, 0, O_TAGGED)) + if (!add_ports(cmd, *av, 0, O_TAGGED, cblen)) errx(EX_DATAERR, "tagged: invalid tag" " list: %s", *av); } @@ -3724,7 +3774,7 @@ read_options: } if (F_LEN(cmd) > 0) { /* prepare to advance */ prev = cmd; - cmd = next_cmd(cmd); + cmd = next_cmd(cmd, &cblen); } } @@ -3753,12 +3803,13 @@ done: */ if (have_state && have_state->opcode != O_CHECK_STATE) { fill_cmd(dst, O_PROBE_STATE, 0, 0); - dst = next_cmd(dst); + dst = next_cmd(dst, &rblen); } /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */ for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { i = F_LEN(src); + CHECK_RBUFLEN(i); switch (src->opcode) { case O_LOG: @@ -3778,6 +3829,7 @@ done: */ if (have_state && have_state->opcode != O_CHECK_STATE) { i = F_LEN(have_state); + CHECK_RBUFLEN(i); bcopy(have_state, dst, i * sizeof(uint32_t)); dst += i; } @@ -3789,24 +3841,29 @@ done: /* put back O_LOG, O_ALTQ, O_TAG if necessary */ if (have_log) { i = F_LEN(have_log); + CHECK_RBUFLEN(i); bcopy(have_log, dst, i * sizeof(uint32_t)); dst += i; } if (have_altq) { i = F_LEN(have_altq); + CHECK_RBUFLEN(i); bcopy(have_altq, dst, i * sizeof(uint32_t)); dst += i; } if (have_tag) { i = F_LEN(have_tag); + CHECK_RBUFLEN(i); bcopy(have_tag, dst, i * sizeof(uint32_t)); dst += i; } + /* * copy all other actions */ for (src = (ipfw_insn *)actbuf; src != action; src += i) { i = F_LEN(src); + CHECK_RBUFLEN(i); bcopy(src, dst, i * sizeof(uint32_t)); dst += i; } @@ -3911,6 +3968,7 @@ ipfw_flush(int force) static void table_list(uint16_t num, int need_header); +static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); /* * This one handles all table-related commands @@ -3926,15 +3984,18 @@ ipfw_table_handler(int ac, char *av[]) int do_add; int is_all; size_t len; - char *p; - uint32_t a, type, mask, addrlen; + uint32_t a; uint32_t tables_max; len = sizeof(tables_max); if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, - NULL, 0) == -1) - errx(1, "Can't determine maximum number of ipfw tables. " - "Perhaps you forgot to load ipfw module?"); + NULL, 0) == -1) { + if (co.test_only) + tables_max = 128; /* Old conservative default */ + else + errx(1, "Can't determine maximum number of ipfw tables." + " Perhaps you forgot to load ipfw module?"); + } memset(&xent, 0, sizeof(xent)); @@ -3963,57 +4024,8 @@ ipfw_table_handler(int ac, char *av[]) ac--; av++; if (!ac) errx(EX_USAGE, "address required"); - /* - * Let's try to guess type by agrument. - * Possible types: - * 1) IPv4[/mask] - * 2) IPv6[/mask] - * 3) interface name - * 4) port ? - */ - type = 0; - if (ishexnumber(*av[0])) { - /* Remove / if exists */ - if ((p = strchr(*av, '/')) != NULL) { - *p = '\0'; - mask = atoi(p + 1); - } - - if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) { - type = IPFW_TABLE_CIDR; - if ((p != NULL) && (mask > 32)) - errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); - xent.masklen = p ? mask : 32; - addrlen = sizeof(struct in_addr); - } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 1) { - type = IPFW_TABLE_CIDR; - if ((p != NULL) && (mask > 128)) - errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); - xent.masklen = p ? mask : 128; - addrlen = sizeof(struct in6_addr); - } - } - if ((type == 0) && (strchr(*av, '.') == NULL)) { - /* Assume interface name. Copy significant data only */ - mask = MIN(strlen(*av), IF_NAMESIZE - 1); - memcpy(xent.k.iface, *av, mask); - /* Set mask to exact match */ - xent.masklen = 8 * IF_NAMESIZE; - type = IPFW_TABLE_INTERFACE; - addrlen = IF_NAMESIZE; - } - - if (type == 0) { - if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) != 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); - xent.masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); - } - - xent.type = type; - xent.len = offsetof(ipfw_table_xentry, k) + addrlen; + table_fill_xentry(*av, &xent); ac--; av++; if (do_add && ac) { @@ -4063,6 +4075,93 @@ ipfw_table_handler(int ac, char *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); + } else { + /* Port or any other key */ + key = strtol(arg, &p, 10); + /* Skip non-base 10 entries like 'fa1' */ + if (p != arg) { + pkey = (uint32_t *)paddr; + *pkey = htonl(key); + type = IPFW_TABLE_CIDR; + addrlen = sizeof(uint32_t); + } + } + } + + 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_list(uint16_t num, int need_header) { ipfw_xtable *tbl; @@ -4105,8 +4204,8 @@ table_list(uint16_t num, int need_header tval = xent->value; addr6 = &xent->k.addr6; - if ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] == 0) && - (addr6->s6_addr32[2] == 0)) { + + if (IN6_IS_ADDR_V4COMPAT(addr6)) { /* IPv4 address */ inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); } else { Modified: stable/9/sbin/ipfw/ipfw2.h ============================================================================== --- stable/9/sbin/ipfw/ipfw2.h Tue Mar 19 13:21:39 2013 (r248504) +++ stable/9/sbin/ipfw/ipfw2.h Tue Mar 19 13:29:01 2013 (r248505) @@ -283,10 +283,10 @@ void print_flow6id(struct _ipfw_insn_u32 void print_icmp6types(struct _ipfw_insn_u32 *cmd); void print_ext6hdr(struct _ipfw_insn *cmd ); -struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av); -struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av); +struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen); +struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen); -void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av ); +void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen); void fill_unreach6_code(u_short *codep, char *str); -void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av); +void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen); int fill_ext6hdr(struct _ipfw_insn *cmd, char *av); Modified: stable/9/sbin/ipfw/ipv6.c ============================================================================== --- stable/9/sbin/ipfw/ipv6.c Tue Mar 19 13:21:39 2013 (r248504) +++ stable/9/sbin/ipfw/ipv6.c Tue Mar 19 13:29:01 2013 (r248505) @@ -42,6 +42,11 @@ #include <netinet/ip_fw.h> #include <arpa/inet.h> +#define CHECK_LENGTH(v, len) do { \ + if ((v) < (len)) \ + errx(EX_DATAERR, "Rule too long"); \ + } while (0) + static struct _s_x icmp6codes[] = { { "no-route", ICMP6_DST_UNREACH_NOROUTE }, { "admin-prohib", ICMP6_DST_UNREACH_ADMIN }, @@ -131,10 +136,12 @@ print_ip6(ipfw_insn_ip6 *cmd, char const } void -fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av) +fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen) { uint8_t type; + CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6)); + bzero(cmd, sizeof(*cmd)); while (*av) { if (*av == ',') @@ -327,7 +334,7 @@ lookup_host6 (char *host, struct in6_add * Return 1 on success, 0 on failure. */ static int -fill_ip6(ipfw_insn_ip6 *cmd, char *av) +fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen) { int len = 0; struct in6_addr *d = &(cmd->addr6); @@ -379,6 +386,8 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av) int masklen; char md = '\0'; + CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr)); + if ((p = strpbrk(av, "/,")) ) { md = *p; /* save the separator */ *p = '\0'; /* terminate address string */ @@ -453,7 +462,7 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av) * additional flow-id we want to filter, the basic is 1 */ void -fill_flow6( ipfw_insn_u32 *cmd, char *av ) +fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen) { u_int32_t type; /* Current flow number */ u_int16_t nflow = 0; /* Current flow index */ @@ -461,6 +470,8 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av cmd->d[0] = 0; /* Initializing the base number*/ while (s) { + CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1); + av = strsep( &s, ",") ; type = strtoul(av, &av, 0); if (*av != ',' && *av != '\0') @@ -481,10 +492,10 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av } ipfw_insn * -add_srcip6(ipfw_insn *cmd, char *av) +add_srcip6(ipfw_insn *cmd, char *av, int cblen) { - fill_ip6((ipfw_insn_ip6 *)cmd, av); + fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ @@ -503,10 +514,10 @@ add_srcip6(ipfw_insn *cmd, char *av) } ipfw_insn * -add_dstip6(ipfw_insn *cmd, char *av) +add_dstip6(ipfw_insn *cmd, char *av, int cblen) { - fill_ip6((ipfw_insn_ip6 *)cmd, av); + fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
I don't see how table_fill_xentry can ever produce an entry containing a port. netinet/ip_fw.h: #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 */ Up to FreeBSD 10 this seems to be the only two types allowed. I don't see how port lookups could ever work in this case: # ipfw table all flush # ipfw table 50 add 1.2.3.4/15 # ipfw table 51 add smtp # ipfw table 52 add 25 # ipfw table 53 add ue0 # ipfw table 54 add %ue0 # ipfw table all list table(50) 1.2.0.0/15 0 table(51) smtp 0 table(52) ::/0 0 table(53) ue0 0 table(54) %ue0 0 I guess the strings are considered network interfaces. I would have expected port numbers to be converted to integers before going into the kernel. Regards, Nick Hibma AnyWi Technologies
New version of ipfw in HEAD/11 has this issue solved. 10:57 [3] m@devel0 s ipfw table ports create type number algo number:array 10:57 [3] m@devel0 s ipfw table ports add 22 added: 22 0 10:58 [3] m@devel0 s ipfw add 1 count tcp from me to any lookup dst-port ports 00001 count tcp from me to lookup dst-port ports 10:58 [3] m@devel0 s ipfw show 1 00001 0 0 count tcp from me to lookup dst-port ports 10:58 [3] m@devel0 telnet 1.2.3.4 22 Trying 1.2.3.4... ^C 10:58 [3] m@devel0 s ipfw show 1 00001 1 60 count tcp from me to lookup dst-port ports
There was a commit referencing this bug, but it's still not closed and has been inactive for some time. Closing as fixed. Please re-open it if the issue hasn't been completely resolved.