Index: projects/ipfw/sbin/ipfw/nat.c =================================================================== --- projects/ipfw/sbin/ipfw/nat.c (revision 271230) +++ projects/ipfw/sbin/ipfw/nat.c (revision 271231) @@ -1,990 +1,1115 @@ /* * Copyright (c) 2002-2003 Luigi Rizzo * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp * Copyright (c) 1994 Ugen J.S.Antsilevich * * Idea and grammar partially left from: * Copyright (c) 1993 Daniel Boulet * * Redistribution and use in source forms, with and without modification, * are permitted provided that this entire comment appears intact. * * Redistribution in binary form may occur without any restrictions. * Obviously, it would be nice if you gave credit where credit is due * but requiring it would be too onerous. * * This software is provided ``AS IS'' without any warranties of any kind. * * NEW command line interface for IP firewall facility * * $FreeBSD$ * * In-kernel nat support */ #include #include #include #include "ipfw2.h" #include #include +#include #include #include #include #include #include -#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */ - #include #include #include /* def. of struct route */ #include #include #include #include +typedef int (nat_cb_t)(struct nat44_cfg_nat *cfg, void *arg); +static void nat_show_cfg(struct nat44_cfg_nat *n, void *arg); +static void nat_show_log(struct nat44_cfg_nat *n, void *arg); +static int nat_show_data(struct nat44_cfg_nat *cfg, void *arg); +static int natname_cmp(const void *a, const void *b); +static int nat_foreach(nat_cb_t *f, void *arg, int sort); +static int nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh); + static struct _s_x nat_params[] = { { "ip", TOK_IP }, { "if", TOK_IF }, { "log", TOK_ALOG }, { "deny_in", TOK_DENY_INC }, { "same_ports", TOK_SAME_PORTS }, { "unreg_only", TOK_UNREG_ONLY }, { "skip_global", TOK_SKIP_GLOBAL }, { "reset", TOK_RESET_ADDR }, { "reverse", TOK_ALIAS_REV }, { "proxy_only", TOK_PROXY_ONLY }, { "redirect_addr", TOK_REDIR_ADDR }, { "redirect_port", TOK_REDIR_PORT }, { "redirect_proto", TOK_REDIR_PROTO }, { NULL, 0 } /* terminator */ }; /* * Search for interface with name "ifn", and fill n accordingly: * * n->ip ip address of interface "ifn" * n->if_name copy of interface name "ifn" */ static void -set_addr_dynamic(const char *ifn, struct cfg_nat *n) +set_addr_dynamic(const char *ifn, struct nat44_cfg_nat *n) { size_t needed; int mib[6]; char *buf, *lim, *next; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr_dl *sdl; struct sockaddr_in *sin; int ifIndex, ifMTU; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_IFLIST; mib[5] = 0; /* * Get interface data. */ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) err(1, "iflist-sysctl-estimate"); buf = safe_calloc(1, needed); if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) err(1, "iflist-sysctl-get"); lim = buf + needed; /* * Loop through interfaces until one with * given name is found. This is done to * find correct interface index for routing * message processing. */ ifIndex = 0; next = buf; while (next < lim) { ifm = (struct if_msghdr *)next; next += ifm->ifm_msglen; if (ifm->ifm_version != RTM_VERSION) { if (co.verbose) warnx("routing message version %d " "not understood", ifm->ifm_version); continue; } if (ifm->ifm_type == RTM_IFINFO) { sdl = (struct sockaddr_dl *)(ifm + 1); if (strlen(ifn) == sdl->sdl_nlen && strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { ifIndex = ifm->ifm_index; ifMTU = ifm->ifm_data.ifi_mtu; break; } } } if (!ifIndex) errx(1, "unknown interface name %s", ifn); /* * Get interface address. */ sin = NULL; while (next < lim) { ifam = (struct ifa_msghdr *)next; next += ifam->ifam_msglen; if (ifam->ifam_version != RTM_VERSION) { if (co.verbose) warnx("routing message version %d " "not understood", ifam->ifam_version); continue; } if (ifam->ifam_type != RTM_NEWADDR) break; if (ifam->ifam_addrs & RTA_IFA) { int i; char *cp = (char *)(ifam + 1); for (i = 1; i < RTA_IFA; i <<= 1) { if (ifam->ifam_addrs & i) cp += SA_SIZE((struct sockaddr *)cp); } if (((struct sockaddr *)cp)->sa_family == AF_INET) { sin = (struct sockaddr_in *)cp; break; } } } if (sin == NULL) errx(1, "%s: cannot get interface address", ifn); n->ip = sin->sin_addr; strncpy(n->if_name, ifn, IF_NAMESIZE); free(buf); } /* * XXX - The following functions, macros and definitions come from natd.c: * it would be better to move them outside natd.c, in a file * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live * with it. */ /* * Definition of a port range, and macros to deal with values. * FORMAT: HI 16-bits == first port in range, 0 == all ports. * LO 16-bits == number of ports in range * NOTES: - Port values are not stored in network byte order. */ #define port_range u_long #define GETLOPORT(x) ((x) >> 0x10) #define GETNUMPORTS(x) ((x) & 0x0000ffff) #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) /* Set y to be the low-port value in port_range variable x. */ #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) /* Set y to be the number of ports in port_range variable x. */ #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) static void StrToAddr (const char* str, struct in_addr* addr) { struct hostent* hp; if (inet_aton (str, addr)) return; hp = gethostbyname (str); if (!hp) errx (1, "unknown host %s", str); memcpy (addr, hp->h_addr, sizeof (struct in_addr)); } static int StrToPortRange (const char* str, const char* proto, port_range *portRange) { char* sep; struct servent* sp; char* end; u_short loPort; u_short hiPort; /* First see if this is a service, return corresponding port if so. */ sp = getservbyname (str,proto); if (sp) { SETLOPORT(*portRange, ntohs(sp->s_port)); SETNUMPORTS(*portRange, 1); return 0; } /* Not a service, see if it's a single port or port range. */ sep = strchr (str, '-'); if (sep == NULL) { SETLOPORT(*portRange, strtol(str, &end, 10)); if (end != str) { /* Single port. */ SETNUMPORTS(*portRange, 1); return 0; } /* Error in port range field. */ errx (EX_DATAERR, "%s/%s: unknown service", str, proto); } /* Port range, get the values and sanity check. */ sscanf (str, "%hu-%hu", &loPort, &hiPort); SETLOPORT(*portRange, loPort); SETNUMPORTS(*portRange, 0); /* Error by default */ if (loPort <= hiPort) SETNUMPORTS(*portRange, hiPort - loPort + 1); if (GETNUMPORTS(*portRange) == 0) errx (EX_DATAERR, "invalid port range %s", str); return 0; } static int StrToProto (const char* str) { if (!strcmp (str, "tcp")) return IPPROTO_TCP; if (!strcmp (str, "udp")) return IPPROTO_UDP; if (!strcmp (str, "sctp")) return IPPROTO_SCTP; errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str); } static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange) { char* ptr; ptr = strchr (str, ':'); if (!ptr) errx (EX_DATAERR, "%s is missing port number", str); *ptr = '\0'; ++ptr; StrToAddr (str, addr); return StrToPortRange (ptr, proto, portRange); } /* End of stuff taken from natd.c. */ /* * The next 3 functions add support for the addr, port and proto redirect and * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() * and SetupProtoRedirect() from natd.c. * * Every setup_* function fills at least one redirect entry - * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool) - * in buf. + * (struct nat44_cfg_redir) and zero or more server pool entry + * (struct nat44_cfg_spool) in buf. * * The format of data in buf is: * - * cfg_nat cfg_redir cfg_spool ...... cfg_spool + * nat44_cfg_nat nat44_cfg_redir nat44_cfg_spool ...... nat44_cfg_spool * * ------------------------------------- ------------ - * | | .....X ... | | | | ..... + * | | .....X ..... | | | | ..... * ------------------------------------- ...... ------------ * ^ * spool_cnt n=0 ...... n=(X-1) * * len points to the amount of available space in buf * space counts the memory consumed by every function * * XXX - Every function get all the argv params so it * has to check, in optional parameters, that the next * args is a valid option for the redir entry and not * another token. Only redir_port and redir_proto are * affected by this. */ static int estimate_redir_addr(int *ac, char ***av) { - size_t space = sizeof(struct cfg_redir); + size_t space = sizeof(struct nat44_cfg_redir); char *sep = **av; u_int c = 0; (void)ac; /* UNUSED */ while ((sep = strchr(sep, ',')) != NULL) { c++; sep++; } if (c > 0) c++; - space += c * sizeof(struct cfg_spool); + space += c * sizeof(struct nat44_cfg_spool); return (space); } static int setup_redir_addr(char *buf, int *ac, char ***av) { - struct cfg_redir *r; + struct nat44_cfg_redir *r; char *sep; size_t space; - r = (struct cfg_redir *)buf; + r = (struct nat44_cfg_redir *)buf; r->mode = REDIR_ADDR; - /* Skip cfg_redir at beginning of buf. */ - buf = &buf[sizeof(struct cfg_redir)]; - space = sizeof(struct cfg_redir); + /* Skip nat44_cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct nat44_cfg_redir)]; + space = sizeof(struct nat44_cfg_redir); /* Extract local address. */ if (strchr(**av, ',') != NULL) { - struct cfg_spool *spool; + struct nat44_cfg_spool *spool; /* Setup LSNAT server pool. */ r->laddr.s_addr = INADDR_NONE; sep = strtok(**av, ","); while (sep != NULL) { - spool = (struct cfg_spool *)buf; - space += sizeof(struct cfg_spool); + spool = (struct nat44_cfg_spool *)buf; + space += sizeof(struct nat44_cfg_spool); StrToAddr(sep, &spool->addr); spool->port = ~0; r->spool_cnt++; - /* Point to the next possible cfg_spool. */ - buf = &buf[sizeof(struct cfg_spool)]; + /* Point to the next possible nat44_cfg_spool. */ + buf = &buf[sizeof(struct nat44_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); + size_t space = sizeof(struct nat44_cfg_redir); char *sep = **av; u_int c = 0; (void)ac; /* UNUSED */ while ((sep = strchr(sep, ',')) != NULL) { c++; sep++; } if (c > 0) c++; - space += c * sizeof(struct cfg_spool); + space += c * sizeof(struct nat44_cfg_spool); return (space); } static int setup_redir_port(char *buf, int *ac, char ***av) { - struct cfg_redir *r; + struct nat44_cfg_redir *r; char *sep, *protoName, *lsnat = NULL; size_t space; u_short numLocalPorts; port_range portRange; numLocalPorts = 0; - r = (struct cfg_redir *)buf; + r = (struct nat44_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); + /* Skip nat44_cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct nat44_cfg_redir)]; + space = sizeof(struct nat44_cfg_redir); /* * Extract protocol. */ r->proto = StrToProto(**av); protoName = **av; (*av)++; (*ac)--; /* * Extract local address. */ if (strchr(**av, ',') != NULL) { r->laddr.s_addr = INADDR_NONE; r->lport = ~0; numLocalPorts = 1; lsnat = **av; } else { /* * 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(**av, ':')) errx(EX_DATAERR, "redirect_port:" "port numbers do not change in sctp, so do " "not specify them as part of the target"); else StrToAddr(**av, &r->laddr); } else { if (StrToAddrAndPortRange(**av, &r->laddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid local port range"); r->lport = GETLOPORT(portRange); numLocalPorts = GETNUMPORTS(portRange); } } (*av)++; (*ac)--; /* * Extract public port and optionally address. */ if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->paddr, protoName, &portRange) != 0) 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: " "invalid public port range"); } r->pport = GETLOPORT(portRange); if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */ numLocalPorts = GETNUMPORTS(portRange); r->lport = r->pport; } r->pport_cnt = GETNUMPORTS(portRange); (*av)++; (*ac)--; /* * Extract remote address and optionally port. */ /* * NB: isdigit(**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 && isdigit(***av)) { if (strchr(**av, ':') != NULL) { if (StrToAddrAndPortRange(**av, &r->raddr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port: " "invalid remote port range"); } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); StrToAddr(**av, &r->raddr); } (*av)++; (*ac)--; } else { SETLOPORT(portRange, 0); SETNUMPORTS(portRange, 1); r->raddr.s_addr = INADDR_ANY; } r->rport = GETLOPORT(portRange); r->rport_cnt = GETNUMPORTS(portRange); /* * Make sure port ranges match up, then add the redirect ports. */ if (numLocalPorts != r->pport_cnt) errx(EX_DATAERR, "redirect_port: " "port ranges must be equal in size"); /* Remote port range is allowed to be '0' which means all ports. */ if (r->rport_cnt != numLocalPorts && (r->rport_cnt != 1 || r->rport != 0)) errx(EX_DATAERR, "redirect_port: remote port must" "be 0 or equal to local port range in size"); /* Setup LSNAT server pool. */ if (lsnat != NULL) { - struct cfg_spool *spool; + struct nat44_cfg_spool *spool; sep = strtok(lsnat, ","); while (sep != NULL) { - spool = (struct cfg_spool *)buf; - space += sizeof(struct cfg_spool); + spool = (struct nat44_cfg_spool *)buf; + space += sizeof(struct nat44_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. */ if (r->proto == IPPROTO_SCTP) { if (strchr (sep, ':')) { errx(EX_DATAERR, "redirect_port:" "port numbers do not change in " "sctp, so do not specify them as " "part of the target"); } else { StrToAddr(sep, &spool->addr); spool->port = r->pport; } } else { if (StrToAddrAndPortRange(sep, &spool->addr, protoName, &portRange) != 0) errx(EX_DATAERR, "redirect_port:" "invalid local port range"); if (GETNUMPORTS(portRange) != 1) errx(EX_DATAERR, "redirect_port: " "local port must be single in " "this context"); spool->port = GETLOPORT(portRange); } r->spool_cnt++; - /* Point to the next possible cfg_spool. */ - buf = &buf[sizeof(struct cfg_spool)]; + /* Point to the next possible nat44_cfg_spool. */ + buf = &buf[sizeof(struct nat44_cfg_spool)]; sep = strtok(NULL, ","); } } return (space); } static int setup_redir_proto(char *buf, int *ac, char ***av) { - struct cfg_redir *r; + struct nat44_cfg_redir *r; struct protoent *protoent; size_t space; - r = (struct cfg_redir *)buf; + r = (struct nat44_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); + /* Skip nat44_cfg_redir at beginning of buf. */ + buf = &buf[sizeof(struct nat44_cfg_redir)]; + space = sizeof(struct nat44_cfg_redir); /* * Extract protocol. */ protoent = getprotobyname(**av); if (protoent == NULL) errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av); else r->proto = protoent->p_proto; (*av)++; (*ac)--; /* * Extract local address. */ StrToAddr(**av, &r->laddr); (*av)++; (*ac)--; /* * Extract optional public address. */ if (*ac == 0) { r->paddr.s_addr = INADDR_ANY; r->raddr.s_addr = INADDR_ANY; } else { /* see above in setup_redir_port() */ if (isdigit(***av)) { StrToAddr(**av, &r->paddr); (*av)++; (*ac)--; /* * Extract optional remote address. */ /* see above in setup_redir_port() */ if (*ac != 0 && isdigit(***av)) { StrToAddr(**av, &r->raddr); (*av)++; (*ac)--; } } } return (space); } static void -print_nat_config(unsigned char *buf) +nat_show_log(struct nat44_cfg_nat *n, void *arg) { - struct cfg_nat *n; + char *buf; + + buf = (char *)(n + 1); + if (buf[0] != '\0') + printf("nat %s: %s\n", n->name, buf); +} + +static void +nat_show_cfg(struct nat44_cfg_nat *n, void *arg) +{ int i, cnt, flag, off; - struct cfg_redir *t; - struct cfg_spool *s; + struct nat44_cfg_redir *t; + struct nat44_cfg_spool *s; + caddr_t buf; struct protoent *p; - n = (struct cfg_nat *)buf; + buf = (caddr_t)n; flag = 1; - off = sizeof(*n); - printf("ipfw nat %u config", n->id); + off = sizeof(*n); + printf("ipfw nat %s config", n->name); if (strlen(n->if_name) != 0) printf(" if %s", n->if_name); else if (n->ip.s_addr != 0) printf(" ip %s", inet_ntoa(n->ip)); while (n->mode != 0) { if (n->mode & PKT_ALIAS_LOG) { printf(" log"); n->mode &= ~PKT_ALIAS_LOG; } else if (n->mode & PKT_ALIAS_DENY_INCOMING) { printf(" deny_in"); n->mode &= ~PKT_ALIAS_DENY_INCOMING; } else if (n->mode & PKT_ALIAS_SAME_PORTS) { printf(" same_ports"); n->mode &= ~PKT_ALIAS_SAME_PORTS; } else if (n->mode & PKT_ALIAS_SKIP_GLOBAL) { printf(" skip_global"); n->mode &= ~PKT_ALIAS_SKIP_GLOBAL; } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) { printf(" unreg_only"); n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY; } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) { printf(" reset"); n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE; } else if (n->mode & PKT_ALIAS_REVERSE) { printf(" reverse"); n->mode &= ~PKT_ALIAS_REVERSE; } else if (n->mode & PKT_ALIAS_PROXY_ONLY) { printf(" proxy_only"); n->mode &= ~PKT_ALIAS_PROXY_ONLY; } } /* Print all the redirect's data configuration. */ for (cnt = 0; cnt < n->redir_cnt; cnt++) { - t = (struct cfg_redir *)&buf[off]; - off += SOF_REDIR; + t = (struct nat44_cfg_redir *)&buf[off]; + off += sizeof(struct nat44_cfg_redir); switch (t->mode) { case REDIR_ADDR: printf(" redirect_addr"); if (t->spool_cnt == 0) printf(" %s", inet_ntoa(t->laddr)); else for (i = 0; i < t->spool_cnt; i++) { - s = (struct cfg_spool *)&buf[off]; + s = (struct nat44_cfg_spool *)&buf[off]; if (i) printf(","); else printf(" "); printf("%s", inet_ntoa(s->addr)); - off += SOF_SPOOL; + off += sizeof(struct nat44_cfg_spool); } printf(" %s", inet_ntoa(t->paddr)); break; case REDIR_PORT: p = getprotobynumber(t->proto); printf(" redirect_port %s ", p->p_name); if (!t->spool_cnt) { printf("%s:%u", inet_ntoa(t->laddr), t->lport); if (t->pport_cnt > 1) printf("-%u", t->lport + t->pport_cnt - 1); } else for (i=0; i < t->spool_cnt; i++) { - s = (struct cfg_spool *)&buf[off]; + s = (struct nat44_cfg_spool *)&buf[off]; if (i) printf(","); printf("%s:%u", inet_ntoa(s->addr), s->port); - off += SOF_SPOOL; + off += sizeof(struct nat44_cfg_spool); } printf(" "); if (t->paddr.s_addr) printf("%s:", inet_ntoa(t->paddr)); printf("%u", t->pport); if (!t->spool_cnt && t->pport_cnt > 1) printf("-%u", t->pport + t->pport_cnt - 1); if (t->raddr.s_addr) { printf(" %s", inet_ntoa(t->raddr)); if (t->rport) { printf(":%u", t->rport); if (!t->spool_cnt && t->rport_cnt > 1) printf("-%u", t->rport + t->rport_cnt - 1); } } break; case REDIR_PROTO: p = getprotobynumber(t->proto); printf(" redirect_proto %s %s", p->p_name, inet_ntoa(t->laddr)); if (t->paddr.s_addr != 0) { printf(" %s", inet_ntoa(t->paddr)); if (t->raddr.s_addr) printf(" %s", inet_ntoa(t->raddr)); } break; default: errx(EX_DATAERR, "unknown redir mode"); break; } } printf("\n"); } void ipfw_config_nat(int ac, char **av) { - struct cfg_nat *n; /* Nat instance configuration. */ + ipfw_obj_header *oh; + struct nat44_cfg_nat *n; /* Nat instance configuration. */ int i, off, tok, ac1; char *id, *buf, **av1, *end; size_t len; av++; ac--; /* Nat id. */ if (ac == 0) errx(EX_DATAERR, "missing nat id"); id = *av; i = (int)strtol(id, &end, 0); if (i <= 0 || *end != '\0') errx(EX_DATAERR, "illegal nat id: %s", id); av++; ac--; if (ac == 0) errx(EX_DATAERR, "missing option"); - len = sizeof(struct cfg_nat); + len = sizeof(*oh) + sizeof(*n); 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_SKIP_GLOBAL: 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; /* Skip optional remoteIP/port */ if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } break; case TOK_REDIR_PROTO: if (ac1 < 2) errx(EX_DATAERR, "redirect_proto: " "not enough arguments"); - len += sizeof(struct cfg_redir); + len += sizeof(struct nat44_cfg_redir); av1 += 2; ac1 -= 2; /* Skip optional remoteIP/port */ if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } if (ac1 != 0 && isdigit(**av1)) { av1++; ac1--; } 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); + /* Offset in buf: save space for header at the beginning. */ + off = sizeof(*oh) + sizeof(*n); memset(buf, 0, len); - n = (struct cfg_nat *)buf; - n->id = i; + oh = (ipfw_obj_header *)buf; + n = (struct nat44_cfg_nat *)(oh + 1); + oh->ntlv.head.length = sizeof(oh->ntlv); + snprintf(oh->ntlv.name, sizeof(oh->ntlv.name), "%d", i); + snprintf(n->name, sizeof(n->name), "%d", i); while (ac > 0) { tok = match_token(nat_params, *av); ac--; av++; switch (tok) { case TOK_IP: if (ac == 0) errx(EX_DATAERR, "missing option"); if (!inet_aton(av[0], &(n->ip))) errx(EX_DATAERR, "bad ip address ``%s''", av[0]); ac--; av++; break; case TOK_IF: if (ac == 0) errx(EX_DATAERR, "missing option"); set_addr_dynamic(av[0], n); ac--; av++; break; case TOK_ALOG: n->mode |= PKT_ALIAS_LOG; break; case TOK_DENY_INC: n->mode |= PKT_ALIAS_DENY_INCOMING; break; case TOK_SAME_PORTS: n->mode |= PKT_ALIAS_SAME_PORTS; break; case TOK_UNREG_ONLY: n->mode |= PKT_ALIAS_UNREGISTERED_ONLY; break; case TOK_SKIP_GLOBAL: n->mode |= PKT_ALIAS_SKIP_GLOBAL; break; case TOK_RESET_ADDR: n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; break; case TOK_ALIAS_REV: n->mode |= PKT_ALIAS_REVERSE; break; case TOK_PROXY_ONLY: n->mode |= PKT_ALIAS_PROXY_ONLY; break; /* * All the setup_redir_* functions work directly in * the final buffer, see above for details. */ case TOK_REDIR_ADDR: case TOK_REDIR_PORT: case TOK_REDIR_PROTO: switch (tok) { case TOK_REDIR_ADDR: i = setup_redir_addr(&buf[off], &ac, &av); break; case TOK_REDIR_PORT: i = setup_redir_port(&buf[off], &ac, &av); break; case TOK_REDIR_PROTO: i = setup_redir_proto(&buf[off], &ac, &av); break; } n->redir_cnt++; off += i; break; } } - i = do_cmd(IP_FW_NAT_CFG, buf, off); - if (i) - err(1, "setsockopt(%s)", "IP_FW_NAT_CFG"); + i = do_set3(IP_FW_NAT44_XCONFIG, &oh->opheader, len); + if (i != 0) + err(1, "setsockopt(%s)", "IP_FW_NAT44_XCONFIG"); if (!co.do_quiet) { /* After every modification, we show the resultant rule. */ int _ac = 3; const char *_av[] = {"show", "config", id}; ipfw_show_nat(_ac, (char **)(void *)_av); } } +struct nat_list_arg { + uint16_t cmd; + int is_all; +}; +static int +nat_show_data(struct nat44_cfg_nat *cfg, void *arg) +{ + struct nat_list_arg *nla; + ipfw_obj_header *oh; + + nla = (struct nat_list_arg *)arg; + + switch (nla->cmd) { + case IP_FW_NAT44_XGETCONFIG: + if (nat_get_cmd(cfg->name, nla->cmd, &oh) != 0) { + warnx("Error getting nat instance %s info", cfg->name); + break; + } + nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL); + free(oh); + break; + case IP_FW_NAT44_XGETLOG: + if (nat_get_cmd(cfg->name, nla->cmd, &oh) == 0) { + nat_show_log((struct nat44_cfg_nat *)(oh + 1), NULL); + free(oh); + break; + } + /* Handle error */ + if (nla->is_all != 0 && errno == ENOENT) + break; + warn("Error getting nat instance %s info", cfg->name); + break; + } + + return (0); +} + +/* + * Compare nat names. + * Honor number comparison. + */ +static int +natname_cmp(const void *a, const void *b) +{ + struct nat44_cfg_nat *ia, *ib; + + ia = (struct nat44_cfg_nat *)a; + ib = (struct nat44_cfg_nat *)b; + + return (stringnum_cmp(ia->name, ib->name)); +} + +/* + * Retrieves nat list from kernel, + * optionally sorts it and calls requested function for each table. + * Returns 0 on success. + */ +static int +nat_foreach(nat_cb_t *f, void *arg, int sort) +{ + ipfw_obj_lheader *olh; + struct nat44_cfg_nat *cfg; + size_t sz; + int i, error; + + /* Start with reasonable default */ + sz = sizeof(*olh) + 16 * sizeof(struct nat44_cfg_nat); + + for (;;) { + if ((olh = calloc(1, sz)) == NULL) + return (ENOMEM); + + olh->size = sz; + if (do_get3(IP_FW_NAT44_LIST_NAT, &olh->opheader, &sz) != 0) { + free(olh); + if (errno == ENOMEM) { + sz = olh->size; + continue; + } + return (errno); + } + + if (sort != 0) + qsort(olh + 1, olh->count, olh->objsize, natname_cmp); + + cfg = (struct nat44_cfg_nat*)(olh + 1); + for (i = 0; i < olh->count; i++) { + error = f(cfg, arg); /* Ignore errors for now */ + cfg = (struct nat44_cfg_nat *)((caddr_t)cfg + + olh->objsize); + } + + free(olh); + break; + } + + return (0); +} + +static int +nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *cfg; + size_t sz; + + /* Start with reasonable default */ + sz = sizeof(*oh) + sizeof(*cfg) + 128; + + for (;;) { + if ((oh = calloc(1, sz)) == NULL) + return (ENOMEM); + cfg = (struct nat44_cfg_nat *)(oh + 1); + oh->ntlv.head.length = sizeof(oh->ntlv); + strlcpy(oh->ntlv.name, name, sizeof(oh->ntlv.name)); + strlcpy(cfg->name, name, sizeof(cfg->name)); + + if (do_get3(cmd, &oh->opheader, &sz) != 0) { + sz = cfg->size; + free(oh); + if (errno == ENOMEM) + continue; + return (errno); + } + + *ooh = oh; + break; + } + + return (0); +} + void ipfw_show_nat(int ac, char **av) { - struct cfg_nat *n; - struct cfg_redir *e; - int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size; - int nat_cnt, redir_cnt, r; - uint8_t *data, *p; - char *endptr; + ipfw_obj_header *oh; + char *name; + int cmd; + struct nat_list_arg nla; - do_rule = 0; - nalloc = 1024; - size = 0; - data = NULL; - frule = 0; - lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */ ac--; av++; if (co.test_only) return; /* Parse parameters. */ - for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) { + cmd = 0; /* XXX: Change to IP_FW_NAT44_XGETLOG @ MFC */ + name = NULL; + for ( ; ac != 0; ac--, av++) { if (!strncmp(av[0], "config", strlen(av[0]))) { - cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1; + cmd = IP_FW_NAT44_XGETCONFIG; continue; } - /* Convert command line rule #. */ - frule = lrule = strtoul(av[0], &endptr, 10); - if (*endptr == '-') - lrule = strtoul(endptr+1, &endptr, 10); - if (lrule == 0) - err(EX_USAGE, "invalid rule number: %s", av[0]); - do_rule = 1; + if (strcmp(av[0], "log") == 0) { + cmd = IP_FW_NAT44_XGETLOG; + continue; + } + if (name != NULL) + err(EX_USAGE,"only one instance name may be specified"); + name = av[0]; } - nbytes = nalloc; - while (nbytes >= nalloc) { - nalloc = nalloc * 2; - nbytes = nalloc; - data = safe_realloc(data, nbytes); - if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0) - err(EX_OSERR, "getsockopt(IP_FW_GET_%s)", - (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG"); - } - if (nbytes == 0) - exit(0); - if (do_cfg) { - nat_cnt = *((int *)data); - for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) { - n = (struct cfg_nat *)&data[i]; - if (frule <= n->id && lrule >= n->id) - print_nat_config(&data[i]); - i += sizeof(struct cfg_nat); - for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) { - e = (struct cfg_redir *)&data[i]; - i += sizeof(struct cfg_redir) + e->spool_cnt * - sizeof(struct cfg_spool); - } - } + if (cmd == 0) + errx(EX_USAGE, "Please specify action. Available: config,log"); + + if (name == NULL) { + memset(&nla, 0, sizeof(nla)); + nla.cmd = cmd; + nla.is_all = 1; + nat_foreach(nat_show_data, &nla, 1); } else { - for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) { - p = &data[i]; - if (p == data + nbytes) - break; - bcopy(p, &r, sizeof(int)); - if (do_rule) { - if (!(frule <= r && lrule >= r)) - continue; - } - printf("nat %u: %s\n", r, p+sizeof(int)); - } + if (nat_get_cmd(name, cmd, &oh) != 0) + err(EX_OSERR, "Error getting nat %s instance info", name); + nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL); + free(oh); } } + Index: projects/ipfw/sys/netinet/ip_fw.h =================================================================== --- projects/ipfw/sys/netinet/ip_fw.h (revision 271230) +++ projects/ipfw/sys/netinet/ip_fw.h (revision 271231) @@ -1,948 +1,994 @@ /*- * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _IPFW2_H #define _IPFW2_H /* * The default rule number. By the design of ip_fw, the default rule * is the last one, so its number can also serve as the highest number * allowed for a rule. The ip_fw code relies on both meanings of this * constant. */ #define IPFW_DEFAULT_RULE 65535 #define RESVD_SET 31 /*set for default and persistent rules*/ #define IPFW_MAX_SETS 32 /* Number of sets supported by ipfw*/ /* * Default number of ipfw tables. */ #define IPFW_TABLES_MAX 65535 #define IPFW_TABLES_DEFAULT 128 /* * Most commands (queue, pipe, tag, untag, limit...) can have a 16-bit * argument between 1 and 65534. The value 0 (IP_FW_TARG) is used * to represent 'tablearg' value, e.g. indicate the use of a 'tablearg' * result of the most recent table() lookup. * Note that 16bit is only a historical limit, resulting from * the use of a 16-bit fields for that value. In reality, we can have * 2^32 pipes, queues, tag values and so on. */ #define IPFW_ARG_MIN 1 #define IPFW_ARG_MAX 65534 #define IP_FW_TABLEARG 65535 /* Compat value for old clients */ #define IP_FW_TARG 0 /* Current tablearg value */ /* * Number of entries in the call stack of the call/return commands. * Call stack currently is an uint16_t array with rule numbers. */ #define IPFW_CALLSTACK_SIZE 16 /* IP_FW3 header/opcodes */ typedef struct _ip_fw3_opheader { uint16_t opcode; /* Operation opcode */ uint16_t version; /* Opcode version */ uint16_t reserved[2]; /* Align to 64-bit boundary */ } ip_fw3_opheader; /* IP_FW3 opcodes */ #define IP_FW_TABLE_XADD 86 /* add entry */ #define IP_FW_TABLE_XDEL 87 /* delete entry */ #define IP_FW_TABLE_XGETSIZE 88 /* get table size (deprecated) */ #define IP_FW_TABLE_XLIST 89 /* list table contents */ #define IP_FW_TABLE_XDESTROY 90 /* destroy table */ #define IP_FW_TABLES_XLIST 92 /* list all tables */ #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_XGET 97 /* Retrieve configuration */ #define IP_FW_XADD 98 /* add rule */ #define IP_FW_XDEL 99 /* del rule */ #define IP_FW_XMOVE 100 /* move rules to different set */ #define IP_FW_XZERO 101 /* clear accounting */ #define IP_FW_XRESETLOG 102 /* zero rules logs */ #define IP_FW_SET_SWAP 103 /* Swap between 2 sets */ #define IP_FW_SET_MOVE 104 /* Move one set to another one */ #define IP_FW_SET_ENABLE 105 /* Enable/disable sets */ #define IP_FW_TABLE_XFIND 106 /* finds an entry */ #define IP_FW_XIFLIST 107 /* list tracked interfaces */ #define IP_FW_TABLES_ALIST 108 /* list table algorithms */ #define IP_FW_TABLE_XSWAP 109 /* swap two tables */ #define IP_FW_TABLE_VLIST 110 /* dump table value hash */ +#define IP_FW_NAT44_XCONFIG 111 /* Create/modify NAT44 instance */ +#define IP_FW_NAT44_DESTROY 112 /* Destroys NAT44 instance */ +#define IP_FW_NAT44_XGETCONFIG 113 /* Get NAT44 instance config */ +#define IP_FW_NAT44_LIST_NAT 114 /* List all NAT44 instances */ +#define IP_FW_NAT44_XGETLOG 115 /* Get log from NAT44 instance */ + /* * The kernel representation of ipfw rules is made of a list of * 'instructions' (for all practical purposes equivalent to BPF * instructions), which specify which fields of the packet * (or its metadata) should be analysed. * * Each instruction is stored in a structure which begins with * "ipfw_insn", and can contain extra fields depending on the * instruction type (listed below). * Note that the code is written so that individual instructions * have a size which is a multiple of 32 bits. This means that, if * such structures contain pointers or other 64-bit entities, * (there is just one instance now) they may end up unaligned on * 64-bit architectures, so the must be handled with care. * * "enum ipfw_opcodes" are the opcodes supported. We can have up * to 256 different opcodes. When adding new opcodes, they should * be appended to the end of the opcode list before O_LAST_OPCODE, * this will prevent the ABI from being broken, otherwise users * will have to recompile ipfw(8) when they update the kernel. */ enum ipfw_opcodes { /* arguments (4 byte each) */ O_NOP, O_IP_SRC, /* u32 = IP */ O_IP_SRC_MASK, /* ip = IP/mask */ O_IP_SRC_ME, /* none */ O_IP_SRC_SET, /* u32=base, arg1=len, bitmap */ O_IP_DST, /* u32 = IP */ O_IP_DST_MASK, /* ip = IP/mask */ O_IP_DST_ME, /* none */ O_IP_DST_SET, /* u32=base, arg1=len, bitmap */ O_IP_SRCPORT, /* (n)port list:mask 4 byte ea */ O_IP_DSTPORT, /* (n)port list:mask 4 byte ea */ O_PROTO, /* arg1=protocol */ O_MACADDR2, /* 2 mac addr:mask */ O_MAC_TYPE, /* same as srcport */ O_LAYER2, /* none */ O_IN, /* none */ O_FRAG, /* none */ O_RECV, /* none */ O_XMIT, /* none */ O_VIA, /* none */ O_IPOPT, /* arg1 = 2*u8 bitmap */ O_IPLEN, /* arg1 = len */ O_IPID, /* arg1 = id */ O_IPTOS, /* arg1 = id */ O_IPPRECEDENCE, /* arg1 = precedence << 5 */ O_IPTTL, /* arg1 = TTL */ O_IPVER, /* arg1 = version */ O_UID, /* u32 = id */ O_GID, /* u32 = id */ O_ESTAB, /* none (tcp established) */ O_TCPFLAGS, /* arg1 = 2*u8 bitmap */ O_TCPWIN, /* arg1 = desired win */ O_TCPSEQ, /* u32 = desired seq. */ O_TCPACK, /* u32 = desired seq. */ O_ICMPTYPE, /* u32 = icmp bitmap */ O_TCPOPTS, /* arg1 = 2*u8 bitmap */ O_VERREVPATH, /* none */ O_VERSRCREACH, /* none */ O_PROBE_STATE, /* none */ O_KEEP_STATE, /* none */ O_LIMIT, /* ipfw_insn_limit */ O_LIMIT_PARENT, /* dyn_type, not an opcode. */ /* * These are really 'actions'. */ O_LOG, /* ipfw_insn_log */ O_PROB, /* u32 = match probability */ O_CHECK_STATE, /* none */ O_ACCEPT, /* none */ O_DENY, /* none */ O_REJECT, /* arg1=icmp arg (same as deny) */ O_COUNT, /* none */ O_SKIPTO, /* arg1=next rule number */ O_PIPE, /* arg1=pipe number */ O_QUEUE, /* arg1=queue number */ O_DIVERT, /* arg1=port number */ O_TEE, /* arg1=port number */ O_FORWARD_IP, /* fwd sockaddr */ O_FORWARD_MAC, /* fwd mac */ O_NAT, /* nope */ O_REASS, /* none */ /* * More opcodes. */ O_IPSEC, /* has ipsec history */ O_IP_SRC_LOOKUP, /* arg1=table number, u32=value */ O_IP_DST_LOOKUP, /* arg1=table number, u32=value */ O_ANTISPOOF, /* none */ O_JAIL, /* u32 = id */ O_ALTQ, /* u32 = altq classif. qid */ O_DIVERTED, /* arg1=bitmap (1:loop, 2:out) */ O_TCPDATALEN, /* arg1 = tcp data len */ O_IP6_SRC, /* address without mask */ O_IP6_SRC_ME, /* my addresses */ O_IP6_SRC_MASK, /* address with the mask */ O_IP6_DST, O_IP6_DST_ME, O_IP6_DST_MASK, O_FLOW6ID, /* for flow id tag in the ipv6 pkt */ O_ICMP6TYPE, /* icmp6 packet type filtering */ O_EXT_HDR, /* filtering for ipv6 extension header */ O_IP6, /* * actions for ng_ipfw */ O_NETGRAPH, /* send to ng_ipfw */ O_NGTEE, /* copy to ng_ipfw */ O_IP4, O_UNREACH6, /* arg1=icmpv6 code arg (deny) */ O_TAG, /* arg1=tag number */ O_TAGGED, /* arg1=tag number */ O_SETFIB, /* arg1=FIB number */ O_FIB, /* arg1=FIB desired fib number */ O_SOCKARG, /* socket argument */ O_CALLRETURN, /* arg1=called rule number */ O_FORWARD_IP6, /* fwd sockaddr_in6 */ 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! */ }; /* * The extension header are filtered only for presence using a bit * vector with a flag for each header. */ #define EXT_FRAGMENT 0x1 #define EXT_HOPOPTS 0x2 #define EXT_ROUTING 0x4 #define EXT_AH 0x8 #define EXT_ESP 0x10 #define EXT_DSTOPTS 0x20 #define EXT_RTHDR0 0x40 #define EXT_RTHDR2 0x80 /* * Template for instructions. * * ipfw_insn is used for all instructions which require no operands, * a single 16-bit value (arg1), or a couple of 8-bit values. * * For other instructions which require different/larger arguments * we have derived structures, ipfw_insn_*. * * The size of the instruction (in 32-bit words) is in the low * 6 bits of "len". The 2 remaining bits are used to implement * NOT and OR on individual instructions. Given a type, you can * compute the length to be put in "len" using F_INSN_SIZE(t) * * F_NOT negates the match result of the instruction. * * F_OR is used to build or blocks. By default, instructions * are evaluated as part of a logical AND. An "or" block * { X or Y or Z } contains F_OR set in all but the last * instruction of the block. A match will cause the code * to skip past the last instruction of the block. * * NOTA BENE: in a couple of places we assume that * sizeof(ipfw_insn) == sizeof(u_int32_t) * this needs to be fixed. * */ typedef struct _ipfw_insn { /* template for instructions */ u_int8_t opcode; u_int8_t len; /* number of 32-bit words */ #define F_NOT 0x80 #define F_OR 0x40 #define F_LEN_MASK 0x3f #define F_LEN(cmd) ((cmd)->len & F_LEN_MASK) u_int16_t arg1; } ipfw_insn; /* * The F_INSN_SIZE(type) computes the size, in 4-byte words, of * a given type. */ #define F_INSN_SIZE(t) ((sizeof (t))/sizeof(u_int32_t)) /* * This is used to store an array of 16-bit entries (ports etc.) */ typedef struct _ipfw_insn_u16 { ipfw_insn o; u_int16_t ports[2]; /* there may be more */ } ipfw_insn_u16; /* * This is used to store an array of 32-bit entries * (uid, single IPv4 addresses etc.) */ typedef struct _ipfw_insn_u32 { ipfw_insn o; u_int32_t d[1]; /* one or more */ } ipfw_insn_u32; /* * This is used to store IP addr-mask pairs. */ typedef struct _ipfw_insn_ip { ipfw_insn o; struct in_addr addr; struct in_addr mask; } ipfw_insn_ip; /* * This is used to forward to a given address (ip). */ typedef struct _ipfw_insn_sa { ipfw_insn o; struct sockaddr_in sa; } ipfw_insn_sa; /* * This is used to forward to a given address (ipv6). */ typedef struct _ipfw_insn_sa6 { ipfw_insn o; struct sockaddr_in6 sa; } ipfw_insn_sa6; /* * This is used for MAC addr-mask pairs. */ typedef struct _ipfw_insn_mac { ipfw_insn o; u_char addr[12]; /* dst[6] + src[6] */ u_char mask[12]; /* dst[6] + src[6] */ } ipfw_insn_mac; /* * This is used for interface match rules (recv xx, xmit xx). */ typedef struct _ipfw_insn_if { ipfw_insn o; union { struct in_addr ip; int glob; uint16_t kidx; } p; char name[IFNAMSIZ]; } ipfw_insn_if; /* * This is used for storing an altq queue id number. */ typedef struct _ipfw_insn_altq { ipfw_insn o; u_int32_t qid; } ipfw_insn_altq; /* * This is used for limit rules. */ typedef struct _ipfw_insn_limit { ipfw_insn o; u_int8_t _pad; u_int8_t limit_mask; /* combination of DYN_* below */ #define DYN_SRC_ADDR 0x1 #define DYN_SRC_PORT 0x2 #define DYN_DST_ADDR 0x4 #define DYN_DST_PORT 0x8 u_int16_t conn_limit; } ipfw_insn_limit; /* * This is used for log instructions. */ typedef struct _ipfw_insn_log { ipfw_insn o; u_int32_t max_log; /* how many do we log -- 0 = all */ u_int32_t log_left; /* how many left to log */ } ipfw_insn_log; +/* Legacy NAT structures, compat only */ +#ifndef _KERNEL /* * Data structures required by both ipfw(8) and ipfw(4) but not part of the * management API are protected by IPFW_INTERNAL. */ #ifdef IPFW_INTERNAL /* Server pool support (LSNAT). */ struct cfg_spool { LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ struct in_addr addr; u_short port; }; #endif /* Redirect modes id. */ #define REDIR_ADDR 0x01 #define REDIR_PORT 0x02 #define REDIR_PROTO 0x04 #ifdef IPFW_INTERNAL /* Nat redirect configuration. */ struct cfg_redir { LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ u_int16_t mode; /* type of redirect mode */ struct in_addr laddr; /* local ip address */ struct in_addr paddr; /* public ip address */ struct in_addr raddr; /* remote ip address */ u_short lport; /* local port */ u_short pport; /* public port */ u_short rport; /* remote port */ u_short pport_cnt; /* number of public ports */ u_short rport_cnt; /* number of remote ports */ int proto; /* protocol: tcp/udp */ struct alias_link **alink; /* num of entry in spool chain */ u_int16_t spool_cnt; /* chain of spool instances */ LIST_HEAD(spool_chain, cfg_spool) spool_chain; }; #endif #ifdef IPFW_INTERNAL /* Nat configuration data struct. */ struct cfg_nat { /* chain of nat instances */ LIST_ENTRY(cfg_nat) _next; int id; /* nat id */ struct in_addr ip; /* nat ip address */ char if_name[IF_NAMESIZE]; /* interface name */ int mode; /* aliasing mode */ struct libalias *lib; /* libalias instance */ /* number of entry in spool chain */ int redir_cnt; /* chain of redir instances */ LIST_HEAD(redir_chain, cfg_redir) redir_chain; }; #endif #define SOF_NAT sizeof(struct cfg_nat) #define SOF_REDIR sizeof(struct cfg_redir) #define SOF_SPOOL sizeof(struct cfg_spool) + +#endif /* ifndef _KERNEL */ + + +struct nat44_cfg_spool { + struct in_addr addr; + uint16_t port; + uint16_t spare; +}; +#define NAT44_REDIR_ADDR 0x01 +#define NAT44_REDIR_PORT 0x02 +#define NAT44_REDIR_PROTO 0x04 + +/* Nat redirect configuration. */ +struct nat44_cfg_redir { + struct in_addr laddr; /* local ip address */ + struct in_addr paddr; /* public ip address */ + struct in_addr raddr; /* remote ip address */ + uint16_t lport; /* local port */ + uint16_t pport; /* public port */ + uint16_t rport; /* remote port */ + uint16_t pport_cnt; /* number of public ports */ + uint16_t rport_cnt; /* number of remote ports */ + uint16_t mode; /* type of redirect mode */ + uint16_t spool_cnt; /* num of entry in spool chain */ + uint16_t spare; + uint32_t proto; /* protocol: tcp/udp */ +}; + +/* Nat configuration data struct. */ +struct nat44_cfg_nat { + char name[64]; /* nat name */ + char if_name[64]; /* interface name */ + uint32_t size; /* structure size incl. redirs */ + struct in_addr ip; /* nat IPv4 address */ + uint32_t mode; /* aliasing mode */ + uint32_t redir_cnt; /* number of entry in spool chain */ +}; /* Nat command. */ typedef struct _ipfw_insn_nat { ipfw_insn o; struct cfg_nat *nat; } ipfw_insn_nat; /* Apply ipv6 mask on ipv6 addr */ #define APPLY_MASK(addr,mask) \ (addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \ (addr)->__u6_addr.__u6_addr32[1] &= (mask)->__u6_addr.__u6_addr32[1]; \ (addr)->__u6_addr.__u6_addr32[2] &= (mask)->__u6_addr.__u6_addr32[2]; \ (addr)->__u6_addr.__u6_addr32[3] &= (mask)->__u6_addr.__u6_addr32[3]; /* Structure for ipv6 */ typedef struct _ipfw_insn_ip6 { ipfw_insn o; struct in6_addr addr6; struct in6_addr mask6; } ipfw_insn_ip6; /* Used to support icmp6 types */ typedef struct _ipfw_insn_icmp6 { ipfw_insn o; uint32_t d[7]; /* XXX This number si related to the netinet/icmp6.h * define ICMP6_MAXTYPE * as follows: n = ICMP6_MAXTYPE/32 + 1 * Actually is 203 */ } ipfw_insn_icmp6; /* * Here we have the structure representing an ipfw rule. * * Layout: * struct ip_fw_rule * [ counter block, size = rule->cntr_len ] * [ one or more instructions, size = rule->cmd_len * 4 ] * * It starts with a general area (with link fields). * Counter block may be next (if rule->cntr_len > 0), * followed by an array of one or more instructions, which the code * accesses as an array of 32-bit values. rule->cmd_len represents * the total instructions legth in u32 worrd, while act_ofs represents * rule action offset in u32 words. * * When assembling instruction, remember the following: * * + if a rule has a "keep-state" (or "limit") option, then the * first instruction (at r->cmd) MUST BE an O_PROBE_STATE * + if a rule has a "log" option, then the first action * (at ACTION_PTR(r)) MUST be O_LOG * + if a rule has an "altq" option, it comes after "log" * + if a rule has an O_TAG option, it comes after "log" and "altq" * * * All structures (excluding instructions) are u64-aligned. * Please keep this. */ struct ip_fw_rule { uint16_t act_ofs; /* offset of action in 32-bit units */ uint16_t cmd_len; /* # of 32-bit words in cmd */ uint16_t spare; uint8_t set; /* rule set (0..31) */ uint8_t flags; /* rule flags */ uint32_t rulenum; /* rule number */ uint32_t id; /* rule id */ ipfw_insn cmd[1]; /* storage for commands */ }; #define IPFW_RULE_NOOPT 0x01 /* Has no options in body */ /* Unaligned version */ /* Base ipfw rule counter block. */ struct ip_fw_bcounter { uint16_t size; /* Size of counter block, bytes */ uint8_t flags; /* flags for given block */ uint8_t spare; uint32_t timestamp; /* tv_sec of last match */ uint64_t pcnt; /* Packet counter */ uint64_t bcnt; /* Byte counter */ }; #ifndef _KERNEL /* * Legacy rule format */ struct ip_fw { struct ip_fw *x_next; /* linked list of rules */ struct ip_fw *next_rule; /* ptr to next [skipto] rule */ /* 'next_rule' is used to pass up 'set_disable' status */ uint16_t act_ofs; /* offset of action in 32-bit units */ uint16_t cmd_len; /* # of 32-bit words in cmd */ uint16_t rulenum; /* rule number */ uint8_t set; /* rule set (0..31) */ uint8_t _pad; /* padding */ uint32_t id; /* rule id */ /* These fields are present in all rules. */ uint64_t pcnt; /* Packet counter */ uint64_t bcnt; /* Byte counter */ uint32_t timestamp; /* tv_sec of last match */ ipfw_insn cmd[1]; /* storage for commands */ }; #endif #define ACTION_PTR(rule) \ (ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) ) #define RULESIZE(rule) (sizeof(*(rule)) + (rule)->cmd_len * 4 - 4) #if 1 // should be moved to in.h /* * This structure is used as a flow mask and a flow id for various * parts of the code. * addr_type is used in userland and kernel to mark the address type. * fib is used in the kernel to record the fib in use. * _flags is used in the kernel to store tcp flags for dynamic rules. */ struct ipfw_flow_id { uint32_t dst_ip; uint32_t src_ip; uint16_t dst_port; uint16_t src_port; uint8_t fib; uint8_t proto; uint8_t _flags; /* protocol-specific flags */ uint8_t addr_type; /* 4=ip4, 6=ip6, 1=ether ? */ struct in6_addr dst_ip6; struct in6_addr src_ip6; uint32_t flow_id6; uint32_t extra; /* queue/pipe or frag_id */ }; #endif #define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6) /* * Dynamic ipfw rule. */ typedef struct _ipfw_dyn_rule ipfw_dyn_rule; struct _ipfw_dyn_rule { ipfw_dyn_rule *next; /* linked list of rules. */ struct ip_fw *rule; /* pointer to rule */ /* 'rule' is used to pass up the rule number (from the parent) */ ipfw_dyn_rule *parent; /* pointer to parent rule */ u_int64_t pcnt; /* packet match counter */ u_int64_t bcnt; /* byte match counter */ struct ipfw_flow_id id; /* (masked) flow id */ u_int32_t expire; /* expire time */ u_int32_t bucket; /* which bucket in hash table */ u_int32_t state; /* state of this rule (typically a * combination of TCP flags) */ u_int32_t ack_fwd; /* most recent ACKs in forward */ u_int32_t ack_rev; /* and reverse directions (used */ /* to generate keepalives) */ u_int16_t dyn_type; /* rule type */ u_int16_t count; /* refcount */ }; /* * Definitions for IP option names. */ #define IP_FW_IPOPT_LSRR 0x01 #define IP_FW_IPOPT_SSRR 0x02 #define IP_FW_IPOPT_RR 0x04 #define IP_FW_IPOPT_TS 0x08 /* * Definitions for TCP option names. */ #define IP_FW_TCPOPT_MSS 0x01 #define IP_FW_TCPOPT_WINDOW 0x02 #define IP_FW_TCPOPT_SACK 0x04 #define IP_FW_TCPOPT_TS 0x08 #define IP_FW_TCPOPT_CC 0x10 #define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */ #define ICMP6_UNREACH_RST 0x100 /* fake ICMPv6 code (send a TCP RST) */ /* * These are used for lookup tables. */ #define IPFW_TABLE_ADDR 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_FLOW 4 /* Table for holding flow data */ #define IPFW_TABLE_MAXTYPE 4 /* Maximum valid number */ #define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */ /* Value types */ #define IPFW_VTYPE_LEGACY 0xFFFFFFFF /* All data is filled in */ #define IPFW_VTYPE_SKIPTO 0x00000001 /* skipto/call/callreturn */ #define IPFW_VTYPE_PIPE 0x00000002 /* pipe/queue */ #define IPFW_VTYPE_FIB 0x00000004 /* setfib */ #define IPFW_VTYPE_NAT 0x00000008 /* nat */ #define IPFW_VTYPE_DSCP 0x00000010 /* dscp */ #define IPFW_VTYPE_TAG 0x00000020 /* tag/untag */ #define IPFW_VTYPE_DIVERT 0x00000040 /* divert/tee */ #define IPFW_VTYPE_NETGRAPH 0x00000080 /* netgraph/ngtee */ #define IPFW_VTYPE_LIMIT 0x00000100 /* IPv6 nexthop */ #define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */ #define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */ typedef struct _ipfw_table_entry { in_addr_t addr; /* network address */ u_int32_t value; /* value */ u_int16_t tbl; /* table number */ u_int8_t masklen; /* mask length */ } ipfw_table_entry; typedef struct _ipfw_table_xentry { uint16_t len; /* Total entry length */ uint8_t type; /* entry type */ uint8_t masklen; /* mask length */ uint16_t tbl; /* table number */ uint16_t flags; /* record flags */ uint32_t value; /* value */ union { /* Longest field needs to be aligned by 4-byte boundary */ struct in6_addr addr6; /* IPv6 address */ char iface[IF_NAMESIZE]; /* interface name */ } k; } ipfw_table_xentry; #define IPFW_TCF_INET 0x01 /* CIDR flags: IPv4 record */ typedef struct _ipfw_table { u_int32_t size; /* size of entries in bytes */ u_int32_t cnt; /* # of entries */ u_int16_t tbl; /* table number */ ipfw_table_entry ent[0]; /* entries */ } ipfw_table; typedef struct _ipfw_xtable { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t size; /* size of entries in bytes */ uint32_t cnt; /* # of entries */ uint16_t tbl; /* table number */ uint8_t type; /* table type */ ipfw_table_xentry xent[0]; /* entries */ } ipfw_xtable; typedef struct _ipfw_obj_tlv { uint16_t type; /* TLV type */ 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_DYNSTATE_LIST 4 #define IPFW_TLV_TBL_ENT 5 #define IPFW_TLV_DYN_ENT 6 #define IPFW_TLV_RULE_ENT 7 #define IPFW_TLV_TBLENT_LIST 8 #define IPFW_TLV_RANGE 9 /* Object name TLV */ typedef struct _ipfw_obj_ntlv { ipfw_obj_tlv head; /* TLV header */ uint16_t idx; /* Name index */ 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; /* 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; }; typedef struct _ipfw_table_value { uint32_t tag; /* O_TAG/O_TAGGED */ uint32_t pipe; /* O_PIPE/O_QUEUE */ uint16_t divert; /* O_DIVERT/O_TEE */ uint16_t skipto; /* skipto, CALLRET */ uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */ uint32_t fib; /* O_SETFIB */ uint32_t nat; /* O_NAT */ uint32_t nh4; uint8_t dscp; uint8_t spare0[3]; struct in6_addr nh6; uint32_t limit; /* O_LIMIT */ uint32_t spare1; uint64_t reserved; } ipfw_table_value; /* 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 */ uint8_t result; /* request result */ uint8_t spare0; uint16_t idx; /* Table name index */ uint16_t spare1; 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 */ struct tflow_entry flow; } k; union { ipfw_table_value value; /* value data */ uint32_t kidx; /* value kernel index */ } v; } ipfw_obj_tentry; #define IPFW_TF_UPDATE 0x01 /* Update record if exists */ /* Container TLV */ #define IPFW_CTF_ATOMIC 0x01 /* Perform atomic operation */ /* Operation results */ #define IPFW_TR_IGNORED 0 /* Entry was ignored (rollback) */ #define IPFW_TR_ADDED 1 /* Entry was succesfully added */ #define IPFW_TR_UPDATED 2 /* Entry was succesfully updated*/ #define IPFW_TR_DELETED 3 /* Entry was succesfully deleted*/ #define IPFW_TR_LIMIT 4 /* Entry was ignored (limit) */ #define IPFW_TR_NOTFOUND 5 /* Entry was not found */ #define IPFW_TR_EXISTS 6 /* Entry already exists */ #define IPFW_TR_ERROR 7 /* Request has failed (unknown) */ typedef struct _ipfw_obj_dyntlv { ipfw_obj_tlv head; ipfw_dyn_rule state; } ipfw_obj_dyntlv; #define IPFW_DF_LAST 0x01 /* Last state in chain */ /* Containter TLVs */ typedef struct _ipfw_obj_ctlv { ipfw_obj_tlv head; /* TLV header */ uint32_t count; /* Number of sub-TLVs */ uint16_t objsize; /* Single object size */ uint8_t version; /* TLV version */ uint8_t flags; /* TLV-specific flags */ } ipfw_obj_ctlv; /* Range TLV */ typedef struct _ipfw_range_tlv { ipfw_obj_tlv head; /* TLV header */ uint32_t flags; /* Range flags */ uint16_t start_rule; /* Range start */ uint16_t end_rule; /* Range end */ uint32_t set; /* Range set to match */ uint32_t new_set; /* New set to move/swap to */ } ipfw_range_tlv; #define IPFW_RCFLAG_RANGE 0x01 /* rule range is set */ #define IPFW_RCFLAG_ALL 0x02 /* match ALL rules */ #define IPFW_RCFLAG_SET 0x04 /* match rules in given set */ typedef struct _ipfw_ta_tinfo { uint32_t flags; /* Format flags */ uint32_t spare; uint8_t taclass4; /* algorithm class */ uint8_t spare4; uint16_t itemsize4; /* item size in runtime */ uint32_t size4; /* runtime structure size */ uint32_t count4; /* number of items in runtime */ uint8_t taclass6; /* algorithm class */ uint8_t spare6; uint16_t itemsize6; /* item size in runtime */ uint32_t size6; /* runtime structure size */ uint32_t count6; /* number of items in runtime */ } ipfw_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_AFDATA 0x0002 /* Separate data per AF */ #define IPFW_TATFLAGS_AFITEM 0x0004 /* diff. items per AF */ typedef struct _ipfw_xtable_info { uint8_t type; /* table type (addr,iface,..) */ uint8_t tflags; /* type flags */ uint16_t mflags; /* modification flags */ uint16_t flags; /* generic table flags */ uint16_t spare[3]; uint32_t vmask; /* bitmask with value types */ 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(export)*/ uint32_t limit; /* Max number of records */ char tablename[64]; /* table name */ char algoname[64]; /* algorithm name */ ipfw_ta_tinfo ta_info; /* additional algo stats */ } ipfw_xtable_info; /* Generic table flags */ #define IPFW_TGFLAGS_LOCKED 0x01 /* Tables is locked from changes*/ /* Table type-specific flags */ #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 /* Table modification flags */ #define IPFW_TMFLAGS_LIMIT 0x0002 /* Change limit value */ #define IPFW_TMFLAGS_LOCK 0x0004 /* Change table lock state */ typedef struct _ipfw_iface_info { char ifname[64]; /* interface name */ uint32_t ifindex; /* interface index */ uint32_t flags; /* flags */ uint32_t refcnt; /* number of references */ uint32_t gencnt; /* number of changes */ uint64_t spare; } ipfw_iface_info; #define IPFW_IFFLAG_RESOLVED 0x01 /* Interface exists */ typedef struct _ipfw_ta_info { char algoname[64]; /* algorithm name */ uint32_t type; /* lookup type */ uint32_t flags; uint32_t refcnt; uint32_t spare0; uint64_t spare1; } ipfw_ta_info; #define IPFW_OBJTYPE_TABLE 1 typedef struct _ipfw_obj_header { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t spare; uint16_t idx; /* object name index */ uint8_t objtype; /* object type */ uint8_t objsubtype; /* object subtype */ ipfw_obj_ntlv ntlv; /* object name tlv */ } ipfw_obj_header; typedef struct _ipfw_obj_lheader { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t set_mask; /* disabled set mask */ uint32_t count; /* Total objects count */ uint32_t size; /* Total size (incl. header) */ uint32_t objsize; /* Size of one object */ } ipfw_obj_lheader; #define IPFW_CFG_GET_STATIC 0x01 #define IPFW_CFG_GET_STATES 0x02 #define IPFW_CFG_GET_COUNTERS 0x04 typedef struct _ipfw_cfg_lheader { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t set_mask; /* enabled set mask */ uint32_t spare; uint32_t flags; /* Request flags */ uint32_t size; /* neded buffer size */ uint32_t start_rule; uint32_t end_rule; } ipfw_cfg_lheader; typedef struct _ipfw_range_header { ip_fw3_opheader opheader; /* IP_FW3 opcode */ ipfw_range_tlv range; } ipfw_range_header; #endif /* _IPFW2_H */ Index: projects/ipfw/sys/netpfil/ipfw/ip_fw_nat.c =================================================================== --- projects/ipfw/sys/netpfil/ipfw/ip_fw_nat.c (revision 271230) +++ projects/ipfw/sys/netpfil/ipfw/ip_fw_nat.c (revision 271231) @@ -1,699 +1,1226 @@ /*- * Copyright (c) 2008 Paolo Pisati * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ - #include #include #include #include #include #include #include #include #include #include #include #include /* XXX for in_cksum */ +struct cfg_spool { + LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ + struct in_addr addr; + uint16_t port; +}; + +/* Nat redirect configuration. */ +struct cfg_redir { + LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ + uint16_t mode; /* type of redirect mode */ + uint16_t proto; /* protocol: tcp/udp */ + struct in_addr laddr; /* local ip address */ + struct in_addr paddr; /* public ip address */ + struct in_addr raddr; /* remote ip address */ + uint16_t lport; /* local port */ + uint16_t pport; /* public port */ + uint16_t rport; /* remote port */ + uint16_t pport_cnt; /* number of public ports */ + uint16_t rport_cnt; /* number of remote ports */ + struct alias_link **alink; + u_int16_t spool_cnt; /* num of entry in spool chain */ + /* chain of spool instances */ + LIST_HEAD(spool_chain, cfg_spool) spool_chain; +}; + +/* Nat configuration data struct. */ +struct cfg_nat { + /* chain of nat instances */ + LIST_ENTRY(cfg_nat) _next; + int id; /* nat id */ + struct in_addr ip; /* nat ip address */ + struct libalias *lib; /* libalias instance */ + int mode; /* aliasing mode */ + int redir_cnt; /* number of entry in spool chain */ + /* chain of redir instances */ + LIST_HEAD(redir_chain, cfg_redir) redir_chain; + char if_name[IF_NAMESIZE]; /* interface name */ +}; + static eventhandler_tag ifaddr_event_tag; static void ifaddr_change(void *arg __unused, struct ifnet *ifp) { struct cfg_nat *ptr; struct ifaddr *ifa; struct ip_fw_chain *chain; KASSERT(curvnet == ifp->if_vnet, ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); chain = &V_layer3_chain; IPFW_WLOCK(chain); /* Check every nat entry... */ LIST_FOREACH(ptr, &chain->nat, _next) { /* ...using nic 'ifp->if_xname' as dynamic alias address. */ if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) continue; if_addr_rlock(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr == NULL) continue; if (ifa->ifa_addr->sa_family != AF_INET) continue; ptr->ip = ((struct sockaddr_in *) (ifa->ifa_addr))->sin_addr; LibAliasSetAddress(ptr->lib, ptr->ip); } if_addr_runlock(ifp); } IPFW_WUNLOCK(chain); } /* * delete the pointers for nat entry ix, or all of them if ix < 0 */ static void flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) { int i; ipfw_insn_nat *cmd; IPFW_WLOCK_ASSERT(chain); for (i = 0; i < chain->n_rules; i++) { cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); /* XXX skip log and the like ? */ if (cmd->o.opcode == O_NAT && cmd->nat != NULL && (ix < 0 || cmd->nat->id == ix)) cmd->nat = NULL; } } static void del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) { struct cfg_redir *r, *tmp_r; struct cfg_spool *s, *tmp_s; int i, num; LIST_FOREACH_SAFE(r, head, _next, tmp_r) { num = 1; /* Number of alias_link to delete. */ switch (r->mode) { - case REDIR_PORT: + case NAT44_REDIR_PORT: num = r->pport_cnt; /* FALLTHROUGH */ - case REDIR_ADDR: - case REDIR_PROTO: + case NAT44_REDIR_ADDR: + case NAT44_REDIR_PROTO: /* Delete all libalias redirect entry. */ for (i = 0; i < num; i++) LibAliasRedirectDelete(n->lib, r->alink[i]); /* Del spool cfg if any. */ LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { LIST_REMOVE(s, _next); free(s, M_IPFW); } free(r->alink, M_IPFW); LIST_REMOVE(r, _next); free(r, M_IPFW); break; default: printf("unknown redirect mode: %u\n", r->mode); /* XXX - panic?!?!? */ break; } } } -static void +static int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) { - struct cfg_redir *r, *ser_r; - struct cfg_spool *s, *ser_s; + struct cfg_redir *r; + struct cfg_spool *s; + struct nat44_cfg_redir *ser_r; + struct nat44_cfg_spool *ser_s; + int cnt, off, i; for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { - ser_r = (struct cfg_redir *)&buf[off]; - r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(r, ser_r, SOF_REDIR); + ser_r = (struct nat44_cfg_redir *)&buf[off]; + r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); + r->mode = ser_r->mode; + r->laddr = ser_r->laddr; + r->paddr = ser_r->paddr; + r->raddr = ser_r->raddr; + r->lport = ser_r->lport; + r->pport = ser_r->pport; + r->rport = ser_r->rport; + r->pport_cnt = ser_r->pport_cnt; + r->rport_cnt = ser_r->rport_cnt; + r->proto = ser_r->proto; + r->spool_cnt = ser_r->spool_cnt; + //memcpy(r, ser_r, SOF_REDIR); LIST_INIT(&r->spool_chain); - off += SOF_REDIR; + off += sizeof(struct nat44_cfg_redir); r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, M_IPFW, M_WAITOK | M_ZERO); switch (r->mode) { - case REDIR_ADDR: + case NAT44_REDIR_ADDR: r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, r->paddr); break; - case REDIR_PORT: + case NAT44_REDIR_PORT: for (i = 0 ; i < r->pport_cnt; i++) { /* If remotePort is all ports, set it to 0. */ u_short remotePortCopy = r->rport + i; if (r->rport_cnt == 1 && r->rport == 0) remotePortCopy = 0; r->alink[i] = LibAliasRedirectPort(ptr->lib, r->laddr, htons(r->lport + i), r->raddr, htons(remotePortCopy), r->paddr, htons(r->pport + i), r->proto); if (r->alink[i] == NULL) { r->alink[0] = NULL; break; } } break; - case REDIR_PROTO: + case NAT44_REDIR_PROTO: r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, r->raddr, r->paddr, r->proto); break; default: printf("unknown redirect mode: %u\n", r->mode); break; } - /* XXX perhaps return an error instead of panic ? */ - if (r->alink[0] == NULL) - panic("LibAliasRedirect* returned NULL"); + if (r->alink[0] == NULL) { + printf("LibAliasRedirect* returned NULL\n"); + return (EINVAL); + } /* LSNAT handling. */ for (i = 0; i < r->spool_cnt; i++) { - ser_s = (struct cfg_spool *)&buf[off]; - s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(s, ser_s, SOF_SPOOL); + ser_s = (struct nat44_cfg_spool *)&buf[off]; + s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); + s->addr = ser_s->addr; + s->port = ser_s->port; LibAliasAddServer(ptr->lib, r->alink[0], s->addr, htons(s->port)); - off += SOF_SPOOL; + off += sizeof(struct nat44_cfg_spool); /* Hook spool entry. */ LIST_INSERT_HEAD(&r->spool_chain, s, _next); } /* And finally hook this redir entry. */ LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); } + + return (0); } /* * ipfw_nat - perform mbuf header translation. * * Note V_layer3_chain has to be locked while calling ipfw_nat() in * 'global' operation mode (t == NULL). * */ static int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) { struct mbuf *mcl; struct ip *ip; /* XXX - libalias duct tape */ int ldt, retval, found; struct ip_fw_chain *chain; char *c; ldt = 0; retval = 0; mcl = m_megapullup(m, m->m_pkthdr.len); if (mcl == NULL) { args->m = NULL; return (IP_FW_DENY); } ip = mtod(mcl, struct ip *); /* * XXX - Libalias checksum offload 'duct tape': * * locally generated packets have only pseudo-header checksum * calculated and libalias will break it[1], so mark them for * later fix. Moreover there are cases when libalias modifies * tcp packet data[2], mark them for later fix too. * * [1] libalias was never meant to run in kernel, so it does * not have any knowledge about checksum offloading, and * expects a packet with a full internet checksum. * Unfortunately, packets generated locally will have just the * pseudo header calculated, and when libalias tries to adjust * the checksum it will actually compute a wrong value. * * [2] when libalias modifies tcp's data content, full TCP * checksum has to be recomputed: the problem is that * libalias does not have any idea about checksum offloading. * To work around this, we do not do checksumming in LibAlias, * but only mark the packets in th_x2 field. If we receive a * marked packet, we calculate correct checksum for it * aware of offloading. Why such a terrible hack instead of * recalculating checksum for each packet? * Because the previous checksum was not checked! * Recalculating checksums for EVERY packet will hide ALL * transmission errors. Yes, marked packets still suffer from * this problem. But, sigh, natd(8) has this problem, too. * * TODO: -make libalias mbuf aware (so * it can handle delayed checksum and tso) */ if (mcl->m_pkthdr.rcvif == NULL && mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) ldt = 1; c = mtod(mcl, char *); /* Check if this is 'global' instance */ if (t == NULL) { if (args->oif == NULL) { /* Wrong direction, skip processing */ args->m = mcl; return (IP_FW_NAT); } found = 0; chain = &V_layer3_chain; IPFW_RLOCK_ASSERT(chain); /* Check every nat entry... */ LIST_FOREACH(t, &chain->nat, _next) { if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) continue; retval = LibAliasOutTry(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl), 0); if (retval == PKT_ALIAS_OK) { /* Nat instance recognises state */ found = 1; break; } } if (found != 1) { /* No instance found, return ignore */ args->m = mcl; return (IP_FW_NAT); } } else { if (args->oif == NULL) retval = LibAliasIn(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl)); else retval = LibAliasOut(t->lib, c, mcl->m_len + M_TRAILINGSPACE(mcl)); } /* * We drop packet when: * 1. libalias returns PKT_ALIAS_ERROR; * 2. For incoming packets: * a) for unresolved fragments; * b) libalias returns PKT_ALIAS_IGNORED and * PKT_ALIAS_DENY_INCOMING flag is set. */ if (retval == PKT_ALIAS_ERROR || (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || (retval == PKT_ALIAS_IGNORED && (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { /* XXX - should i add some logging? */ m_free(mcl); args->m = NULL; return (IP_FW_DENY); } if (retval == PKT_ALIAS_RESPOND) mcl->m_flags |= M_SKIP_FIREWALL; mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); /* * XXX - libalias checksum offload * 'duct tape' (see above) */ if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && ip->ip_p == IPPROTO_TCP) { struct tcphdr *th; th = (struct tcphdr *)(ip + 1); if (th->th_x2) ldt = 1; } if (ldt) { struct tcphdr *th; struct udphdr *uh; uint16_t ip_len, cksum; ip_len = ntohs(ip->ip_len); cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); switch (ip->ip_p) { case IPPROTO_TCP: th = (struct tcphdr *)(ip + 1); /* * Maybe it was set in * libalias... */ th->th_x2 = 0; th->th_sum = cksum; mcl->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum); break; case IPPROTO_UDP: uh = (struct udphdr *)(ip + 1); uh->uh_sum = cksum; mcl->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); break; } /* No hw checksum offloading: do it ourselves */ if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { in_delayed_cksum(mcl); mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; } } args->m = mcl; return (IP_FW_NAT); } static struct cfg_nat * lookup_nat(struct nat_list *l, int nat_id) { struct cfg_nat *res; LIST_FOREACH(res, l, _next) { if (res->id == nat_id) break; } return res; } -static int -ipfw_nat_cfg(struct sockopt *sopt) +static struct cfg_nat * +lookup_nat_name(struct nat_list *l, char *name) { - struct cfg_nat *cfg, *ptr; - char *buf; - struct ip_fw_chain *chain = &V_layer3_chain; - size_t len; - int gencnt, error = 0; + struct cfg_nat *res; + int id; + char *errptr; - len = sopt->sopt_valsize; - buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) - goto out; + id = strtol(name, &errptr, 10); + if (id == 0 || *errptr != '\0') + return (NULL); - cfg = (struct cfg_nat *)buf; - if (cfg->id < 0) { - error = EINVAL; - goto out; + LIST_FOREACH(res, l, _next) { + if (res->id == id) + break; } + return (res); +} +/* IP_FW3 configuration routines */ + +static void +nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) +{ + struct cfg_nat *ptr, *tcfg; + int gencnt; + /* * Find/create nat rule. */ - IPFW_WLOCK(chain); + IPFW_UH_WLOCK(chain); gencnt = chain->gencnt; - ptr = lookup_nat(&chain->nat, cfg->id); + ptr = lookup_nat_name(&chain->nat, ucfg->name); if (ptr == NULL) { - IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); /* New rule: allocate and init new instance. */ ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); ptr->lib = LibAliasInit(NULL); LIST_INIT(&ptr->redir_chain); } else { /* Entry already present: temporarily unhook it. */ + IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); - flush_nat_ptrs(chain, cfg->id); + flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); } /* - * Basic nat configuration. + * Basic nat (re)configuration. */ - ptr->id = cfg->id; + ptr->id = strtol(ucfg->name, NULL, 10); /* * XXX - what if this rule doesn't nat any ip and just * redirect? * do we set aliasaddress to 0.0.0.0? */ - ptr->ip = cfg->ip; - ptr->redir_cnt = cfg->redir_cnt; - ptr->mode = cfg->mode; - LibAliasSetMode(ptr->lib, cfg->mode, ~0); + ptr->ip = ucfg->ip; + ptr->redir_cnt = ucfg->redir_cnt; + ptr->mode = ucfg->mode; + strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); + LibAliasSetMode(ptr->lib, ptr->mode, ~0); LibAliasSetAddress(ptr->lib, ptr->ip); - memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); /* * Redir and LSNAT configuration. */ /* Delete old cfgs. */ del_redir_spool_cfg(ptr, &ptr->redir_chain); /* Add new entries. */ - add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); + add_redir_spool_cfg((char *)(ucfg + 1), ptr); + IPFW_UH_WLOCK(chain); - IPFW_WLOCK(chain); /* Extra check to avoid race with another ipfw_nat_cfg() */ - if (gencnt != chain->gencnt && - ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) - LIST_REMOVE(cfg, _next); + tcfg = NULL; + if (gencnt != chain->gencnt) + tcfg = lookup_nat_name(&chain->nat, ucfg->name); + IPFW_WLOCK(chain); + if (tcfg != NULL) + LIST_REMOVE(tcfg, _next); LIST_INSERT_HEAD(&chain->nat, ptr, _next); + IPFW_WUNLOCK(chain); chain->gencnt++; + + IPFW_UH_WUNLOCK(chain); + + if (tcfg != NULL) + free(tcfg, M_IPFW); +} + +/* + * Creates/configure nat44 instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat .. ] + * + * Returns 0 on success + */ +static int +nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + int id; + size_t read; + char *errptr; + + /* Check minimum header size */ + if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated and looks like number */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + id = strtol(ucfg->name, &errptr, 10); + if (id == 0 || *errptr != '\0') + return (EINVAL); + + read = sizeof(*oh) + sizeof(*ucfg); + /* Check number of redirs */ + if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) + return (EINVAL); + + nat44_config(chain, ucfg); + return (0); +} + +/* + * Destroys given nat instances. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ] + * + * Returns 0 on success + */ +static int +nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct cfg_nat *ptr; + ipfw_obj_ntlv *ntlv; + + /* Check minimum header size */ + if (sd->valsize < sizeof(*oh)) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ntlv = &oh->ntlv; + /* Check if name is properly terminated */ + if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) + return (EINVAL); + + IPFW_UH_WLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ntlv->name); + if (ptr == NULL) { + IPFW_UH_WUNLOCK(chain); + return (ESRCH); + } + IPFW_WLOCK(chain); + LIST_REMOVE(ptr, _next); + flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); + del_redir_spool_cfg(ptr, &ptr->redir_chain); + LibAliasUninit(ptr->lib); + free(ptr, M_IPFW); + + return (0); +} + +static void +export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) +{ + + snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); + ucfg->ip = ptr->ip; + ucfg->redir_cnt = ptr->redir_cnt; + ucfg->mode = ptr->mode; + strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); +} + +/* + * Gets config for given nat instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat .. ] + * + * Returns 0 on success + */ +static int +nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + struct cfg_redir *r; + struct cfg_spool *s; + struct nat44_cfg_redir *ser_r; + struct nat44_cfg_spool *ser_s; + size_t sz; + + sz = sizeof(*oh) + sizeof(*ucfg); + /* Check minimum header size */ + if (sd->valsize < sz) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + + IPFW_UH_RLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ucfg->name); + if (ptr == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ESRCH); + } + + export_nat_cfg(ptr, ucfg); + + /* Estimate memory amount */ + sz = sizeof(struct nat44_cfg_nat); + LIST_FOREACH(r, &ptr->redir_chain, _next) { + sz += sizeof(struct nat44_cfg_redir); + LIST_FOREACH(s, &r->spool_chain, _next) + sz += sizeof(struct nat44_cfg_spool); + } + + ucfg->size = sz; + if (sd->valsize < sz + sizeof(*oh)) { + + /* + * Submitted buffer size is not enough. + * WE've already filled in @ucfg structure with + * relevant info including size, so we + * can return. Buffer will be flushed automatically. + */ + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + /* Size OK, let's copy data */ + LIST_FOREACH(r, &ptr->redir_chain, _next) { + ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, + sizeof(*ser_r)); + ser_r->mode = r->mode; + ser_r->laddr = r->laddr; + ser_r->paddr = r->paddr; + ser_r->raddr = r->raddr; + ser_r->lport = r->lport; + ser_r->pport = r->pport; + ser_r->rport = r->rport; + ser_r->pport_cnt = r->pport_cnt; + ser_r->rport_cnt = r->rport_cnt; + ser_r->proto = r->proto; + ser_r->spool_cnt = r->spool_cnt; + + LIST_FOREACH(s, &r->spool_chain, _next) { + ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( + sd, sizeof(*ser_s)); + + ser_s->addr = s->addr; + ser_s->port = s->port; + } + } + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +/* + * Lists all nat44 instances currently available in kernel. + * Data layout (v0)(current): + * Request: [ ipfw_obj_lheader ] + * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] + * + * Returns 0 on success + */ +static int +nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_lheader *olh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + int nat_count; + + /* Check minimum header size */ + if (sd->valsize < sizeof(ipfw_obj_lheader)) + return (EINVAL); + + olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); + IPFW_UH_RLOCK(chain); + nat_count = 0; + LIST_FOREACH(ptr, &chain->nat, _next) + nat_count++; + + olh->count = nat_count; + olh->objsize = sizeof(struct nat44_cfg_nat); + olh->size = sizeof(*olh) + olh->count * olh->objsize; + + if (sd->valsize < olh->size) { + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + LIST_FOREACH(ptr, &chain->nat, _next) { + ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, + sizeof(*ucfg)); + export_nat_cfg(ptr, ucfg); + } + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +/* + * Gets log for given nat instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat ] + * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] + * + * Returns 0 on success + */ +static int +nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + void *pbuf; + size_t sz; + + sz = sizeof(*oh) + sizeof(*ucfg); + /* Check minimum header size */ + if (sd->valsize < sz) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + + IPFW_UH_RLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ucfg->name); + if (ptr == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ESRCH); + } + + if (ptr->lib->logDesc == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ENOENT); + } + + export_nat_cfg(ptr, ucfg); + + /* Estimate memory amount */ + ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; + if (sd->valsize < sz + sizeof(*oh)) { + + /* + * Submitted buffer size is not enough. + * WE've already filled in @ucfg structure with + * relevant info including size, so we + * can return. Buffer will be flushed automatically. + */ + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); + memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +static struct ipfw_sopt_handler scodes[] = { + { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, + { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, + { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, + { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, + { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, +}; + + +/* + * Legacy configuration routines + */ + +struct cfg_spool_legacy { + LIST_ENTRY(cfg_spool_legacy) _next; + struct in_addr addr; + u_short port; +}; + +struct cfg_redir_legacy { + LIST_ENTRY(cfg_redir) _next; + u_int16_t mode; + struct in_addr laddr; + struct in_addr paddr; + struct in_addr raddr; + u_short lport; + u_short pport; + u_short rport; + u_short pport_cnt; + u_short rport_cnt; + int proto; + struct alias_link **alink; + u_int16_t spool_cnt; + LIST_HEAD(, cfg_spool_legacy) spool_chain; +}; + +struct cfg_nat_legacy { + LIST_ENTRY(cfg_nat_legacy) _next; + int id; + struct in_addr ip; + char if_name[IF_NAMESIZE]; + int mode; + struct libalias *lib; + int redir_cnt; + LIST_HEAD(, cfg_redir_legacy) redir_chain; +}; + +static int +ipfw_nat_cfg(struct sockopt *sopt) +{ + struct cfg_nat_legacy *cfg; + struct nat44_cfg_nat *ucfg; + struct cfg_redir_legacy *rdir; + struct nat44_cfg_redir *urdir; + char *buf; + size_t len, len2; + int error, i; + + len = sopt->sopt_valsize; + len2 = len + 128; + + /* + * Allocate 2x buffer to store converted structures. + * new redir_cfg has shrinked, so we're sure that + * new buffer size is enough. + */ + buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); + error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); + if (error != 0) + goto out; + + cfg = (struct cfg_nat_legacy *)buf; + if (cfg->id < 0) { + error = EINVAL; + goto out; + } + + ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; + snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); + strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); + ucfg->ip = cfg->ip; + ucfg->mode = cfg->mode; + ucfg->redir_cnt = cfg->redir_cnt; + + if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { + error = EINVAL; + goto out; + } + + urdir = (struct nat44_cfg_redir *)(ucfg + 1); + rdir = (struct cfg_redir_legacy *)(cfg + 1); + for (i = 0; i < cfg->redir_cnt; i++) { + urdir->mode = rdir->mode; + urdir->laddr = rdir->laddr; + urdir->paddr = rdir->paddr; + urdir->raddr = rdir->raddr; + urdir->lport = rdir->lport; + urdir->pport = rdir->pport; + urdir->rport = rdir->rport; + urdir->pport_cnt = rdir->pport_cnt; + urdir->rport_cnt = rdir->rport_cnt; + urdir->proto = rdir->proto; + urdir->spool_cnt = rdir->spool_cnt; + + urdir++; + rdir++; + } + + nat44_config(&V_layer3_chain, ucfg); + out: free(buf, M_TEMP); return (error); } static int ipfw_nat_del(struct sockopt *sopt) { struct cfg_nat *ptr; struct ip_fw_chain *chain = &V_layer3_chain; int i; sooptcopyin(sopt, &i, sizeof i, sizeof i); /* XXX validate i */ - IPFW_WLOCK(chain); + IPFW_UH_WLOCK(chain); ptr = lookup_nat(&chain->nat, i); if (ptr == NULL) { - IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); return (EINVAL); } + IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); flush_nat_ptrs(chain, i); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); del_redir_spool_cfg(ptr, &ptr->redir_chain); LibAliasUninit(ptr->lib); free(ptr, M_IPFW); return (0); } static int ipfw_nat_get_cfg(struct sockopt *sopt) { struct ip_fw_chain *chain = &V_layer3_chain; struct cfg_nat *n; + struct cfg_nat_legacy *ucfg; struct cfg_redir *r; struct cfg_spool *s; + struct cfg_redir_legacy *ser_r; + struct cfg_spool_legacy *ser_s; char *data; int gencnt, nat_cnt, len, error; nat_cnt = 0; len = sizeof(nat_cnt); - IPFW_RLOCK(chain); + IPFW_UH_RLOCK(chain); retry: gencnt = chain->gencnt; /* Estimate memory amount */ LIST_FOREACH(n, &chain->nat, _next) { nat_cnt++; - len += sizeof(struct cfg_nat); + len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { - len += sizeof(struct cfg_redir); + len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) - len += sizeof(struct cfg_spool); + len += sizeof(struct cfg_spool_legacy); } } - IPFW_RUNLOCK(chain); + IPFW_UH_RUNLOCK(chain); data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); bcopy(&nat_cnt, data, sizeof(nat_cnt)); nat_cnt = 0; len = sizeof(nat_cnt); - IPFW_RLOCK(chain); + IPFW_UH_RLOCK(chain); if (gencnt != chain->gencnt) { free(data, M_TEMP); goto retry; } /* Serialize all the data. */ LIST_FOREACH(n, &chain->nat, _next) { - bcopy(n, &data[len], sizeof(struct cfg_nat)); - len += sizeof(struct cfg_nat); + ucfg = (struct cfg_nat_legacy *)&data[len]; + ucfg->id = n->id; + ucfg->ip = n->ip; + ucfg->redir_cnt = n->redir_cnt; + ucfg->mode = n->mode; + strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); + len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { - bcopy(r, &data[len], sizeof(struct cfg_redir)); - len += sizeof(struct cfg_redir); + ser_r = (struct cfg_redir_legacy *)&data[len]; + ser_r->mode = r->mode; + ser_r->laddr = r->laddr; + ser_r->paddr = r->paddr; + ser_r->raddr = r->raddr; + ser_r->lport = r->lport; + ser_r->pport = r->pport; + ser_r->rport = r->rport; + ser_r->pport_cnt = r->pport_cnt; + ser_r->rport_cnt = r->rport_cnt; + ser_r->proto = r->proto; + ser_r->spool_cnt = r->spool_cnt; + len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) { - bcopy(s, &data[len], sizeof(struct cfg_spool)); - len += sizeof(struct cfg_spool); + ser_s = (struct cfg_spool_legacy *)&data[len]; + ser_s->addr = s->addr; + ser_s->port = s->port; + len += sizeof(struct cfg_spool_legacy); } } } - IPFW_RUNLOCK(chain); + IPFW_UH_RUNLOCK(chain); error = sooptcopyout(sopt, data, len); free(data, M_TEMP); return (error); } static int ipfw_nat_get_log(struct sockopt *sopt) { uint8_t *data; struct cfg_nat *ptr; int i, size; struct ip_fw_chain *chain; chain = &V_layer3_chain; IPFW_RLOCK(chain); /* one pass to count, one to copy the data */ i = 0; LIST_FOREACH(ptr, &chain->nat, _next) { if (ptr->lib->logDesc == NULL) continue; i++; } size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); if (data == NULL) { IPFW_RUNLOCK(chain); return (ENOSPC); } i = 0; LIST_FOREACH(ptr, &chain->nat, _next) { if (ptr->lib->logDesc == NULL) continue; bcopy(&ptr->id, &data[i], sizeof(int)); i += sizeof(int); bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); i += LIBALIAS_BUF_SIZE; } IPFW_RUNLOCK(chain); sooptcopyout(sopt, data, size); free(data, M_IPFW); return(0); } static int vnet_ipfw_nat_init(const void *arg __unused) { V_ipfw_nat_ready = 1; return (0); } static int vnet_ipfw_nat_uninit(const void *arg __unused) { struct cfg_nat *ptr, *ptr_temp; struct ip_fw_chain *chain; chain = &V_layer3_chain; IPFW_WLOCK(chain); LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { LIST_REMOVE(ptr, _next); del_redir_spool_cfg(ptr, &ptr->redir_chain); LibAliasUninit(ptr->lib); free(ptr, M_IPFW); } flush_nat_ptrs(chain, -1 /* flush all */); V_ipfw_nat_ready = 0; IPFW_WUNLOCK(chain); return (0); } static void ipfw_nat_init(void) { /* init ipfw hooks */ ipfw_nat_ptr = ipfw_nat; lookup_nat_ptr = lookup_nat; ipfw_nat_cfg_ptr = ipfw_nat_cfg; ipfw_nat_del_ptr = ipfw_nat_del; ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; ipfw_nat_get_log_ptr = ipfw_nat_get_log; + IPFW_ADD_SOPT_HANDLER(1, scodes); ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, NULL, EVENTHANDLER_PRI_ANY); } static void ipfw_nat_destroy(void) { EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); /* deregister ipfw_nat */ + IPFW_DEL_SOPT_HANDLER(1, scodes); ipfw_nat_ptr = NULL; lookup_nat_ptr = NULL; ipfw_nat_cfg_ptr = NULL; ipfw_nat_del_ptr = NULL; ipfw_nat_get_cfg_ptr = NULL; ipfw_nat_get_log_ptr = NULL; } static int ipfw_nat_modevent(module_t mod, int type, void *unused) { int err = 0; switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: break; default: return EOPNOTSUPP; break; } return err; } static moduledata_t ipfw_nat_mod = { "ipfw_nat", ipfw_nat_modevent, 0 }; /* Define startup order. */ #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); MODULE_VERSION(ipfw_nat, 1); SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, ipfw_nat_init, NULL); VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_init, NULL); SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, ipfw_nat_destroy, NULL); VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); /* end of file */