From owner-svn-src-all@FreeBSD.ORG Tue Apr 19 15:03:12 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 487501065674; Tue, 19 Apr 2011 15:03:12 +0000 (UTC) (envelope-from glebius@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 36B0E8FC15; Tue, 19 Apr 2011 15:03:12 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id p3JF3C4Y011368; Tue, 19 Apr 2011 15:03:12 GMT (envelope-from glebius@svn.freebsd.org) Received: (from glebius@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id p3JF3Cck011366; Tue, 19 Apr 2011 15:03:12 GMT (envelope-from glebius@svn.freebsd.org) Message-Id: <201104191503.p3JF3Cck011366@svn.freebsd.org> From: Gleb Smirnoff Date: Tue, 19 Apr 2011 15:03:12 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r220835 - head/sbin/ipfw X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Apr 2011 15:03:12 -0000 Author: glebius Date: Tue Apr 19 15:03:12 2011 New Revision: 220835 URL: http://svn.freebsd.org/changeset/base/220835 Log: Rewrite NAT configuration parser, so that memory allocation size is calculated dynamically. PR: kern/143653 Modified: head/sbin/ipfw/nat.c Modified: head/sbin/ipfw/nat.c ============================================================================== --- head/sbin/ipfw/nat.c Tue Apr 19 13:54:51 2011 (r220834) +++ head/sbin/ipfw/nat.c Tue Apr 19 15:03:12 2011 (r220835) @@ -281,13 +281,6 @@ StrToAddrAndPortRange (const char* str, /* End of stuff taken from natd.c. */ -#define INC_ARGCV() do { \ - (*_av)++; \ - (*_ac)--; \ - av = *_av; \ - ac = *_ac; \ -} while(0) - /* * The next 3 functions add support for the addr, port and proto redirect and * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() @@ -318,121 +311,107 @@ StrToAddrAndPortRange (const char* str, */ static int -setup_redir_addr(char *spool_buf, unsigned int len, - int *_ac, char ***_av) +estimate_redir_addr(int *ac, char ***av) +{ + size_t space = sizeof(struct cfg_redir); + char *sep; + + if ((sep = strtok(**av, ",")) != NULL) { + space += sizeof(struct cfg_spool); + while ((sep = strtok(NULL, ",")) != NULL) + space += sizeof(struct cfg_spool); + } + + return (space); +} + +static int +setup_redir_addr(char *buf, int *ac, char ***av) { - char **av, *sep; /* Token separator. */ - /* Temporary buffer used to hold server pool ip's. */ - char tmp_spool_buf[NAT_BUF_LEN]; - int ac, space, lsnat; struct cfg_redir *r; - struct cfg_spool *tmp; + char *sep; + size_t space; - av = *_av; - ac = *_ac; - space = 0; - lsnat = 0; - if (len >= SOF_REDIR) { - r = (struct cfg_redir *)spool_buf; - /* Skip cfg_redir at beginning of buf. */ - spool_buf = &spool_buf[SOF_REDIR]; - space = SOF_REDIR; - len -= SOF_REDIR; - } else - goto nospace; + r = (struct cfg_redir *)buf; r->mode = REDIR_ADDR; - /* Extract local address. */ - if (ac == 0) - errx(EX_DATAERR, "redirect_addr: missing local address"); - sep = strchr(*av, ','); - if (sep) { /* LSNAT redirection syntax. */ - r->laddr.s_addr = INADDR_NONE; - /* Preserve av, copy spool servers to tmp_spool_buf. */ - strncpy(tmp_spool_buf, *av, strlen(*av)+1); - lsnat = 1; - } else - StrToAddr(*av, &r->laddr); - INC_ARGCV(); + /* Skip cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct cfg_redir)]; + space = sizeof(struct cfg_redir); - /* Extract public address. */ - if (ac == 0) - errx(EX_DATAERR, "redirect_addr: missing public address"); - StrToAddr(*av, &r->paddr); - INC_ARGCV(); + /* Extract local address. */ + if ((sep = strtok(**av, ",")) != NULL) { + struct cfg_spool *spool; - /* Setup LSNAT server pool. */ - if (sep) { - sep = strtok(tmp_spool_buf, ","); + /* Setup LSNAT server pool. */ + r->laddr.s_addr = INADDR_NONE; while (sep != NULL) { - tmp = (struct cfg_spool *)spool_buf; - if (len < SOF_SPOOL) - goto nospace; - len -= SOF_SPOOL; - space += SOF_SPOOL; - StrToAddr(sep, &tmp->addr); - tmp->port = ~0; + spool = (struct cfg_spool *)buf; + space += sizeof(struct cfg_spool); + StrToAddr(sep, &spool->addr); + spool->port = ~0; r->spool_cnt++; /* Point to the next possible cfg_spool. */ - spool_buf = &spool_buf[SOF_SPOOL]; + buf = &buf[sizeof(struct cfg_spool)]; sep = strtok(NULL, ","); } + } else + StrToAddr(**av, &r->laddr); + (*av)++; (*ac)--; + + /* Extract public address. */ + StrToAddr(**av, &r->paddr); + (*av)++; (*ac)--; + + return (space); +} + +static int +estimate_redir_port(int *ac, char ***av) +{ + size_t space = sizeof(struct cfg_redir); + char *sep; + + if ((sep = strtok(**av, ",")) != NULL) { + space += sizeof(struct cfg_spool); + while ((sep = strtok(NULL, ",")) != NULL) + space += sizeof(struct cfg_spool); } - return(space); -nospace: - errx(EX_DATAERR, "redirect_addr: buf is too small\n"); + + return (space); } static int -setup_redir_port(char *spool_buf, unsigned int len, - int *_ac, char ***_av) +setup_redir_port(char *buf, int *ac, char ***av) { - char **av, *sep, *protoName; - char tmp_spool_buf[NAT_BUF_LEN]; - int ac, space, lsnat; struct cfg_redir *r; - struct cfg_spool *tmp; + char *sep, *protoName, *lsnat = NULL; + size_t space; u_short numLocalPorts; port_range portRange; - av = *_av; - ac = *_ac; - space = 0; - lsnat = 0; numLocalPorts = 0; - if (len >= SOF_REDIR) { - r = (struct cfg_redir *)spool_buf; - /* Skip cfg_redir at beginning of buf. */ - spool_buf = &spool_buf[SOF_REDIR]; - space = SOF_REDIR; - len -= SOF_REDIR; - } else - goto nospace; + r = (struct cfg_redir *)buf; r->mode = REDIR_PORT; + /* Skip cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct cfg_redir)]; + space = sizeof(struct cfg_redir); + /* * Extract protocol. */ - if (ac == 0) - errx (EX_DATAERR, "redirect_port: missing protocol"); - r->proto = StrToProto(*av); - protoName = *av; - INC_ARGCV(); + r->proto = StrToProto(**av); + protoName = **av; + (*av)++; (*ac)--; /* * Extract local address. */ - if (ac == 0) - errx (EX_DATAERR, "redirect_port: missing local address"); - - sep = strchr(*av, ','); - /* LSNAT redirection syntax. */ - if (sep) { + if ((sep = strchr(**av, ',')) != NULL) { r->laddr.s_addr = INADDR_NONE; r->lport = ~0; numLocalPorts = 1; - /* Preserve av, copy spool servers to tmp_spool_buf. */ - strncpy(tmp_spool_buf, *av, strlen(*av)+1); - lsnat = 1; + lsnat = **av; } else { /* * The sctp nat does not allow the port numbers to be mapped to @@ -440,40 +419,36 @@ setup_redir_port(char *spool_buf, unsign * in the target port field. */ if (r->proto == IPPROTO_SCTP) { - if (strchr (*av, ':')) + if (strchr(**av, ':')) errx(EX_DATAERR, "redirect_port:" - "port numbers do not change in sctp, so do not " - "specify them as part of the target"); + "port numbers do not change in sctp, so do " + "not specify them as part of the target"); else - StrToAddr(*av, &r->laddr); + StrToAddr(**av, &r->laddr); } else { - if (StrToAddrAndPortRange (*av, &r->laddr, protoName, - &portRange) != 0) - errx(EX_DATAERR, "redirect_port:" + if (StrToAddrAndPortRange(**av, &r->laddr, protoName, + &portRange) != 0) + errx(EX_DATAERR, "redirect_port: " "invalid local port range"); r->lport = GETLOPORT(portRange); numLocalPorts = GETNUMPORTS(portRange); } } - INC_ARGCV(); + (*av)++; (*ac)--; /* * Extract public port and optionally address. */ - if (ac == 0) - errx (EX_DATAERR, "redirect_port: missing public port"); - - sep = strchr (*av, ':'); - if (sep) { - if (StrToAddrAndPortRange (*av, &r->paddr, protoName, + if ((sep = strchr(**av, ':')) != NULL) { + if (StrToAddrAndPortRange(**av, &r->paddr, protoName, &portRange) != 0) - errx(EX_DATAERR, "redirect_port:" + errx(EX_DATAERR, "redirect_port: " "invalid public port range"); } else { r->paddr.s_addr = INADDR_ANY; - if (StrToPortRange (*av, protoName, &portRange) != 0) - errx(EX_DATAERR, "redirect_port:" + if (StrToPortRange(**av, protoName, &portRange) != 0) + errx(EX_DATAERR, "redirect_port: " "invalid public port range"); } @@ -483,7 +458,7 @@ setup_redir_port(char *spool_buf, unsign r->lport = r->pport; } r->pport_cnt = GETNUMPORTS(portRange); - INC_ARGCV(); + (*av)++; (*ac)--; /* * Extract remote address and optionally port. @@ -492,19 +467,18 @@ setup_redir_port(char *spool_buf, unsign * NB: isalpha(**av) => we've to check that next parameter is really an * option for this redirect entry, else stop here processing arg[cv]. */ - if (ac != 0 && !isalpha(**av)) { - sep = strchr (*av, ':'); - if (sep) { - if (StrToAddrAndPortRange (*av, &r->raddr, protoName, + if (*ac != 0 && !isalpha(***av)) { + if ((sep = strchr(**av, ':')) != NULL) { + if (StrToAddrAndPortRange(**av, &r->raddr, protoName, &portRange) != 0) - errx(EX_DATAERR, "redirect_port:" + errx(EX_DATAERR, "redirect_port: " "invalid remote port range"); } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); - StrToAddr (*av, &r->raddr); + StrToAddr(**av, &r->raddr); } - INC_ARGCV(); + (*av)++; (*ac)--; } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); @@ -517,7 +491,7 @@ setup_redir_port(char *spool_buf, unsign * Make sure port ranges match up, then add the redirect ports. */ if (numLocalPorts != r->pport_cnt) - errx(EX_DATAERR, "redirect_port:" + errx(EX_DATAERR, "redirect_port: " "port ranges must be equal in size"); /* Remote port range is allowed to be '0' which means all ports. */ @@ -526,20 +500,18 @@ setup_redir_port(char *spool_buf, unsign errx(EX_DATAERR, "redirect_port: remote port must" "be 0 or equal to local port range in size"); - /* - * Setup LSNAT server pool. - */ - if (lsnat) { - sep = strtok(tmp_spool_buf, ","); + /* Setup LSNAT server pool. */ + if (lsnat != NULL) { + struct cfg_spool *spool; + + sep = strtok(lsnat, ","); while (sep != NULL) { - tmp = (struct cfg_spool *)spool_buf; - if (len < SOF_SPOOL) - goto nospace; - len -= SOF_SPOOL; - space += SOF_SPOOL; + spool = (struct cfg_spool *)buf; + space += sizeof(struct cfg_spool); /* - * The sctp nat does not allow the port numbers to be mapped to new port numbers - * Therefore, no ports are to be specified in the target port field + * The sctp nat does not allow the port numbers to + * be mapped to new port numbers. Therefore, no ports + * are to be specified in the target port field. */ if (r->proto == IPPROTO_SCTP) { if (strchr (sep, ':')) { @@ -548,11 +520,11 @@ setup_redir_port(char *spool_buf, unsign "sctp, so do not specify them as " "part of the target"); } else { - StrToAddr(sep, &tmp->addr); - tmp->port = r->pport; + StrToAddr(sep, &spool->addr); + spool->port = r->pport; } } else { - if (StrToAddrAndPortRange(sep, &tmp->addr, + if (StrToAddrAndPortRange(sep, &spool->addr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port:" "invalid local port range"); @@ -560,88 +532,73 @@ setup_redir_port(char *spool_buf, unsign errx(EX_DATAERR, "redirect_port: " "local port must be single in " "this context"); - tmp->port = GETLOPORT(portRange); + spool->port = GETLOPORT(portRange); } r->spool_cnt++; /* Point to the next possible cfg_spool. */ - spool_buf = &spool_buf[SOF_SPOOL]; + buf = &buf[sizeof(struct cfg_spool)]; sep = strtok(NULL, ","); } } + return (space); -nospace: - errx(EX_DATAERR, "redirect_port: buf is too small\n"); } static int -setup_redir_proto(char *spool_buf, unsigned int len, - int *_ac, char ***_av) +setup_redir_proto(char *buf, int *ac, char ***av) { - char **av; - int ac, space; - struct protoent *protoent; struct cfg_redir *r; + struct protoent *protoent; + size_t space; - av = *_av; - ac = *_ac; - if (len >= SOF_REDIR) { - r = (struct cfg_redir *)spool_buf; - /* Skip cfg_redir at beginning of buf. */ - spool_buf = &spool_buf[SOF_REDIR]; - space = SOF_REDIR; - len -= SOF_REDIR; - } else - goto nospace; + r = (struct cfg_redir *)buf; r->mode = REDIR_PROTO; + /* Skip cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct cfg_redir)]; + space = sizeof(struct cfg_redir); + /* * Extract protocol. */ - if (ac == 0) - errx(EX_DATAERR, "redirect_proto: missing protocol"); - - protoent = getprotobyname(*av); + protoent = getprotobyname(**av); if (protoent == NULL) - errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av); + errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av); else r->proto = protoent->p_proto; - INC_ARGCV(); + (*av)++; (*ac)--; /* * Extract local address. */ - if (ac == 0) - errx(EX_DATAERR, "redirect_proto: missing local address"); - else - StrToAddr(*av, &r->laddr); + StrToAddr(**av, &r->laddr); - INC_ARGCV(); + (*av)++; (*ac)--; /* * Extract optional public address. */ - if (ac == 0) { + if (*ac == 0) { r->paddr.s_addr = INADDR_ANY; r->raddr.s_addr = INADDR_ANY; } else { /* see above in setup_redir_port() */ - if (!isalpha(**av)) { - StrToAddr(*av, &r->paddr); - INC_ARGCV(); + if (!isalpha(***av)) { + StrToAddr(**av, &r->paddr); + (*av)++; (*ac)--; /* * Extract optional remote address. */ /* see above in setup_redir_port() */ - if (ac!=0 && !isalpha(**av)) { - StrToAddr(*av, &r->raddr); - INC_ARGCV(); + if (*ac != 0 && !isalpha(***av)) { + StrToAddr(**av, &r->raddr); + (*av)++; (*ac)--; } } } + return (space); -nospace: - errx(EX_DATAERR, "redirect_proto: buf is too small\n"); } static void @@ -763,27 +720,76 @@ void ipfw_config_nat(int ac, char **av) { struct cfg_nat *n; /* Nat instance configuration. */ - int i, len, off, tok; - char *id, buf[NAT_BUF_LEN]; /* Buffer for serialized data. */ - - len = NAT_BUF_LEN; - /* Offset in buf: save space for n at the beginning. */ - off = sizeof(*n); - memset(buf, 0, sizeof(buf)); - n = (struct cfg_nat *)buf; + int i, off, tok, ac1; + char *id, *buf, **av1; + size_t len; av++; ac--; /* Nat id. */ if (ac && isdigit(**av)) { id = *av; - i = atoi(*av); ac--; av++; - n->id = i; } else errx(EX_DATAERR, "missing nat id"); if (ac == 0) errx(EX_DATAERR, "missing option"); + len = sizeof(struct cfg_nat); + ac1 = ac; + av1 = av; + while (ac1 > 0) { + tok = match_token(nat_params, *av1); + ac1--; av1++; + switch (tok) { + case TOK_IP: + case TOK_IF: + ac1--; av1++; + break; + case TOK_ALOG: + case TOK_DENY_INC: + case TOK_SAME_PORTS: + case TOK_UNREG_ONLY: + case TOK_RESET_ADDR: + case TOK_ALIAS_REV: + case TOK_PROXY_ONLY: + break; + case TOK_REDIR_ADDR: + if (ac1 < 2) + errx(EX_DATAERR, "redirect_addr: " + "not enough arguments"); + len += estimate_redir_addr(&ac1, &av1); + av1 += 2; ac1 -= 2; + break; + case TOK_REDIR_PORT: + if (ac1 < 3) + errx(EX_DATAERR, "redirect_port: " + "not enough arguments"); + av1++; ac1--; + len += estimate_redir_port(&ac1, &av1); + av1 += 2; ac1 -= 2; + break; + case TOK_REDIR_PROTO: + if (ac1 < 2) + errx(EX_DATAERR, "redirect_proto: " + "not enough arguments"); + len += sizeof(struct cfg_redir); + av1 += 2; ac1 -= 2; + break; + default: + errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]); + } + } + + if ((buf = malloc(len)) == NULL) + errx(EX_OSERR, "malloc failed"); + + /* Offset in buf: save space for n at the beginning. */ + off = sizeof(*n); + memset(buf, 0, len); + n = (struct cfg_nat *)buf; + i = atoi(id); + n->id = i; + while (ac > 0) { tok = match_token(nat_params, *av); ac--; av++; @@ -832,21 +838,18 @@ ipfw_config_nat(int ac, char **av) case TOK_REDIR_PROTO: switch (tok) { case TOK_REDIR_ADDR: - i = setup_redir_addr(&buf[off], len, &ac, &av); + i = setup_redir_addr(&buf[off], &ac, &av); break; case TOK_REDIR_PORT: - i = setup_redir_port(&buf[off], len, &ac, &av); + i = setup_redir_port(&buf[off], &ac, &av); break; case TOK_REDIR_PROTO: - i = setup_redir_proto(&buf[off], len, &ac, &av); + i = setup_redir_proto(&buf[off], &ac, &av); break; } n->redir_cnt++; off += i; - len -= i; break; - default: - errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); } }