nat.c revision 187983
1226298Sedwin/* 2192886Sedwin * Copyright (c) 2002-2003 Luigi Rizzo 3192886Sedwin * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 464499Swollman * Copyright (c) 1994 Ugen J.S.Antsilevich 52742Swollman * 62742Swollman * Idea and grammar partially left from: 72742Swollman * Copyright (c) 1993 Daniel Boulet 82742Swollman * 9158421Swollman * Redistribution and use in source forms, with and without modification, 102742Swollman * are permitted provided that this entire comment appears intact. 112742Swollman * 12158421Swollman * Redistribution in binary form may occur without any restrictions. 13158421Swollman * Obviously, it would be nice if you gave credit where credit is due 142742Swollman * but requiring it would be too onerous. 1586222Swollman * 1620094Swollman * This software is provided ``AS IS'' without any warranties of any kind. 1720094Swollman * 1820094Swollman * NEW command line interface for IP firewall facility 1920094Swollman * 2020094Swollman * $FreeBSD: head/sbin/ipfw/nat.c 187983 2009-02-01 16:00:49Z luigi $ 21158421Swollman * 22158421Swollman * In-kernel nat support 2320094Swollman */ 242742Swollman 252742Swollman#include <sys/types.h> 262742Swollman#include <sys/socket.h> 272742Swollman#include <sys/sysctl.h> 282742Swollman 2958787Sru#include "ipfw2.h" 302742Swollman 312742Swollman#include <ctype.h> 322742Swollman#include <err.h> 332742Swollman#include <netdb.h> 34114173Swollman#include <stdio.h> 35114173Swollman#include <stdlib.h> 36114173Swollman#include <string.h> 37114173Swollman#include <sysexits.h> 38114173Swollman 39114173Swollman#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */ 40114173Swollman 41114173Swollman#include <net/if.h> 42114173Swollman#include <net/if_dl.h> 43114173Swollman#include <net/route.h> /* def. of struct route */ 44114173Swollman#include <netinet/in.h> 45114173Swollman#include <netinet/ip_fw.h> 46114173Swollman#include <arpa/inet.h> 47114173Swollman#include <alias.h> 48149590Swollman 49149590Swollmanstatic struct _s_x nat_params[] = { 50114173Swollman { "ip", TOK_IP }, 512742Swollman { "if", TOK_IF }, 529908Swollman { "log", TOK_ALOG }, 532742Swollman { "deny_in", TOK_DENY_INC }, 542742Swollman { "same_ports", TOK_SAME_PORTS }, 552742Swollman { "unreg_only", TOK_UNREG_ONLY }, 562742Swollman { "reset", TOK_RESET_ADDR }, 572742Swollman { "reverse", TOK_ALIAS_REV }, 582742Swollman { "proxy_only", TOK_PROXY_ONLY }, 592742Swollman { "redirect_addr", TOK_REDIR_ADDR }, 602742Swollman { "redirect_port", TOK_REDIR_PORT }, 612742Swollman { "redirect_proto", TOK_REDIR_PROTO }, 6220094Swollman { NULL, 0 } /* terminator */ 632742Swollman}; 6420094Swollman 65158421Swollman 6620094Swollman/* 6720094Swollman * Search for interface with name "ifn", and fill n accordingly: 6820094Swollman * 6920094Swollman * n->ip ip address of interface "ifn" 7020094Swollman * n->if_name copy of interface name "ifn" 7120094Swollman */ 7220094Swollmanstatic void 7320094Swollmanset_addr_dynamic(const char *ifn, struct cfg_nat *n) 7420094Swollman{ 7520094Swollman size_t needed; 7620094Swollman int mib[6]; 7720094Swollman char *buf, *lim, *next; 7820094Swollman struct if_msghdr *ifm; 792742Swollman struct ifa_msghdr *ifam; 80223629Sedwin struct sockaddr_dl *sdl; 81223629Sedwin struct sockaddr_in *sin; 82223629Sedwin int ifIndex, ifMTU; 83223629Sedwin 842742Swollman mib[0] = CTL_NET; 852742Swollman mib[1] = PF_ROUTE; 862742Swollman mib[2] = 0; 8719878Swollman mib[3] = AF_INET; 882742Swollman mib[4] = NET_RT_IFLIST; 892742Swollman mib[5] = 0; 902742Swollman/* 91158421Swollman * Get interface data. 92158421Swollman */ 93158421Swollman if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) 94158421Swollman err(1, "iflist-sysctl-estimate"); 95158421Swollman buf = safe_calloc(1, needed); 96153670Swollman if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) 9743014Swollman err(1, "iflist-sysctl-get"); 9843014Swollman lim = buf + needed; 9943014Swollman/* 1002742Swollman * Loop through interfaces until one with 1012742Swollman * given name is found. This is done to 10219878Swollman * find correct interface index for routing 10319878Swollman * message processing. 10419878Swollman */ 10558787Sru ifIndex = 0; 10643014Swollman next = buf; 10775267Swollman while (next < lim) { 1082742Swollman ifm = (struct if_msghdr *)next; 1092742Swollman next += ifm->ifm_msglen; 110153670Swollman if (ifm->ifm_version != RTM_VERSION) { 111153670Swollman if (co.verbose) 112153670Swollman warnx("routing message version %d " 11343014Swollman "not understood", ifm->ifm_version); 114153670Swollman continue; 115153670Swollman } 1162742Swollman if (ifm->ifm_type == RTM_IFINFO) { 1172742Swollman sdl = (struct sockaddr_dl *)(ifm + 1); 11819878Swollman if (strlen(ifn) == sdl->sdl_nlen && 11919878Swollman strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { 12019878Swollman ifIndex = ifm->ifm_index; 121149514Swollman ifMTU = ifm->ifm_data.ifi_mtu; 12220094Swollman break; 12343014Swollman } 12443014Swollman } 1252742Swollman } 1262742Swollman if (!ifIndex) 1272742Swollman errx(1, "unknown interface name %s", ifn); 12867578Swollman/* 1292742Swollman * Get interface address. 1302742Swollman */ 1312742Swollman sin = NULL; 1322742Swollman while (next < lim) { 133193785Sedwin ifam = (struct ifa_msghdr *)next; 134193785Sedwin next += ifam->ifam_msglen; 135193785Sedwin if (ifam->ifam_version != RTM_VERSION) { 136193785Sedwin if (co.verbose) 137193785Sedwin warnx("routing message version %d " 138193785Sedwin "not understood", ifam->ifam_version); 139193785Sedwin continue; 140193785Sedwin } 141193785Sedwin if (ifam->ifam_type != RTM_NEWADDR) 142193785Sedwin break; 143193785Sedwin if (ifam->ifam_addrs & RTA_IFA) { 144193785Sedwin int i; 145193785Sedwin char *cp = (char *)(ifam + 1); 146193785Sedwin 147193785Sedwin for (i = 1; i < RTA_IFA; i <<= 1) { 148193785Sedwin if (ifam->ifam_addrs & i) 149193785Sedwin cp += SA_SIZE((struct sockaddr *)cp); 150193785Sedwin } 151193785Sedwin if (((struct sockaddr *)cp)->sa_family == AF_INET) { 152193785Sedwin sin = (struct sockaddr_in *)cp; 153193785Sedwin break; 154193785Sedwin } 155193785Sedwin } 156193785Sedwin } 157193785Sedwin if (sin == NULL) 158193785Sedwin errx(1, "%s: cannot get interface address", ifn); 159193785Sedwin 160193785Sedwin n->ip = sin->sin_addr; 161193785Sedwin strncpy(n->if_name, ifn, IF_NAMESIZE); 162193785Sedwin 163193785Sedwin free(buf); 164193785Sedwin} 165193785Sedwin 166193785Sedwin/* 167193785Sedwin * XXX - The following functions, macros and definitions come from natd.c: 168193785Sedwin * it would be better to move them outside natd.c, in a file 169193785Sedwin * (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live 170193785Sedwin * with it. 171194485Sedwin */ 172194485Sedwin 173194485Sedwin/* 174194485Sedwin * Definition of a port range, and macros to deal with values. 175194485Sedwin * FORMAT: HI 16-bits == first port in range, 0 == all ports. 176194485Sedwin * LO 16-bits == number of ports in range 177193785Sedwin * NOTES: - Port values are not stored in network byte order. 178198270Sedwin */ 179198270Sedwin 180198270Sedwin#define port_range u_long 181198270Sedwin 182198270Sedwin#define GETLOPORT(x) ((x) >> 0x10) 183198270Sedwin#define GETNUMPORTS(x) ((x) & 0x0000ffff) 184198270Sedwin#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) 185198270Sedwin 186198270Sedwin/* Set y to be the low-port value in port_range variable x. */ 187198270Sedwin#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) 188198270Sedwin 189198270Sedwin/* Set y to be the number of ports in port_range variable x. */ 190198270Sedwin#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) 191196581Sedwin 192198270Sedwinstatic void 193198270SedwinStrToAddr (const char* str, struct in_addr* addr) 194198270Sedwin{ 195198270Sedwin struct hostent* hp; 196198270Sedwin 197198270Sedwin if (inet_aton (str, addr)) 198198270Sedwin return; 199198270Sedwin 200198270Sedwin hp = gethostbyname (str); 201198270Sedwin if (!hp) 202198270Sedwin errx (1, "unknown host %s", str); 203201189Sedwin 204201189Sedwin memcpy (addr, hp->h_addr, sizeof (struct in_addr)); 205201189Sedwin} 206201189Sedwin 207201189Sedwinstatic int 208201189SedwinStrToPortRange (const char* str, const char* proto, port_range *portRange) 209201189Sedwin{ 210201189Sedwin char* sep; 211201189Sedwin struct servent* sp; 212201189Sedwin char* end; 213201189Sedwin u_short loPort; 214201189Sedwin u_short hiPort; 215201189Sedwin 216201189Sedwin /* First see if this is a service, return corresponding port if so. */ 217201189Sedwin sp = getservbyname (str,proto); 218201189Sedwin if (sp) { 219201189Sedwin SETLOPORT(*portRange, ntohs(sp->s_port)); 220201189Sedwin SETNUMPORTS(*portRange, 1); 221206219Sedwin return 0; 222206219Sedwin } 223206219Sedwin 224206219Sedwin /* Not a service, see if it's a single port or port range. */ 225206219Sedwin sep = strchr (str, '-'); 226204887Sedwin if (sep == NULL) { 227206219Sedwin SETLOPORT(*portRange, strtol(str, &end, 10)); 228206219Sedwin if (end != str) { 229206219Sedwin /* Single port. */ 230206219Sedwin SETNUMPORTS(*portRange, 1); 231204887Sedwin return 0; 232201189Sedwin } 233202606Sedwin 234204887Sedwin /* Error in port range field. */ 235201189Sedwin errx (EX_DATAERR, "%s/%s: unknown service", str, proto); 2362742Swollman } 23775267Swollman 23819878Swollman /* Port range, get the values and sanity check. */ 23919878Swollman sscanf (str, "%hu-%hu", &loPort, &hiPort); 2402742Swollman SETLOPORT(*portRange, loPort); 24119878Swollman SETNUMPORTS(*portRange, 0); /* Error by default */ 24219878Swollman if (loPort <= hiPort) 243201189Sedwin SETNUMPORTS(*portRange, hiPort - loPort + 1); 244201189Sedwin 2452742Swollman if (GETNUMPORTS(*portRange) == 0) 2462742Swollman errx (EX_DATAERR, "invalid port range %s", str); 2472742Swollman 24867578Swollman return 0; 2492742Swollman} 25019878Swollman 2512742Swollmanstatic int 2522742SwollmanStrToProto (const char* str) 25386222Swollman{ 25486222Swollman if (!strcmp (str, "tcp")) 255149514Swollman return IPPROTO_TCP; 256149514Swollman 257149514Swollman if (!strcmp (str, "udp")) 2582742Swollman return IPPROTO_UDP; 259149514Swollman 260149514Swollman errx (EX_DATAERR, "unknown protocol %s. Expected tcp or udp", str); 26186222Swollman} 2622742Swollman 2632742Swollmanstatic int 2642742SwollmanStrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, 2652742Swollman port_range *portRange) 2662742Swollman{ 2672742Swollman char* ptr; 2682742Swollman 26914343Swollman ptr = strchr (str, ':'); 2702742Swollman if (!ptr) 27117200Swollman errx (EX_DATAERR, "%s is missing port number", str); 27219878Swollman 27319878Swollman *ptr = '\0'; 2742742Swollman ++ptr; 27519878Swollman 2762742Swollman StrToAddr (str, addr); 2772742Swollman return StrToPortRange (ptr, proto, portRange); 2782742Swollman} 2792742Swollman 28019878Swollman/* End of stuff taken from natd.c. */ 2812742Swollman 2822742Swollman#define INC_ARGCV() do { \ 2832742Swollman (*_av)++; \ 2842742Swollman (*_ac)--; \ 28543014Swollman av = *_av; \ 2862742Swollman ac = *_ac; \ 2872742Swollman} while(0) 2882742Swollman 2892742Swollman/* 29019878Swollman * The next 3 functions add support for the addr, port and proto redirect and 29119878Swollman * their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() 2922742Swollman * and SetupProtoRedirect() from natd.c. 2932742Swollman * 2942742Swollman * Every setup_* function fills at least one redirect entry 29593799Swollman * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool) 2962742Swollman * in buf. 2972742Swollman * 2982742Swollman * The format of data in buf is: 2992742Swollman * 3002742Swollman * 3012742Swollman * cfg_nat cfg_redir cfg_spool ...... cfg_spool 3022742Swollman * 3032742Swollman * ------------------------------------- ------------ 30419878Swollman * | | .....X ... | | | | ..... 3052742Swollman * ------------------------------------- ...... ------------ 3062742Swollman * ^ 3072742Swollman * spool_cnt n=0 ...... n=(X-1) 308158421Swollman * 309158421Swollman * len points to the amount of available space in buf 310158421Swollman * space counts the memory consumed by every function 311158421Swollman * 3122742Swollman * XXX - Every function get all the argv params so it 313158421Swollman * has to check, in optional parameters, that the next 314158421Swollman * args is a valid option for the redir entry and not 3152742Swollman * another token. Only redir_port and redir_proto are 316158421Swollman * affected by this. 3172742Swollman */ 3182742Swollman 3192742Swollmanstatic int 3202742Swollmansetup_redir_addr(char *spool_buf, int len, 3212742Swollman int *_ac, char ***_av) 32214343Swollman{ 32314343Swollman char **av, *sep; /* Token separator. */ 324163302Sru /* Temporary buffer used to hold server pool ip's. */ 32593799Swollman char tmp_spool_buf[NAT_BUF_LEN]; 32693799Swollman int ac, space, lsnat; 32793799Swollman struct cfg_redir *r; 328163302Sru struct cfg_spool *tmp; 329169811Swollman 330163302Sru av = *_av; 331163302Sru ac = *_ac; 332163302Sru space = 0; 333163302Sru lsnat = 0; 334163302Sru if (len >= SOF_REDIR) { 335163302Sru r = (struct cfg_redir *)spool_buf; 336163302Sru /* Skip cfg_redir at beginning of buf. */ 337163302Sru spool_buf = &spool_buf[SOF_REDIR]; 338163302Sru space = SOF_REDIR; 339163302Sru len -= SOF_REDIR; 340163302Sru } else 341181421Sedwin goto nospace; 342181421Sedwin r->mode = REDIR_ADDR; 343181421Sedwin /* Extract local address. */ 344181421Sedwin if (ac == 0) 345181421Sedwin errx(EX_DATAERR, "redirect_addr: missing local address"); 346181421Sedwin sep = strchr(*av, ','); 347181421Sedwin if (sep) { /* LSNAT redirection syntax. */ 348181421Sedwin r->laddr.s_addr = INADDR_NONE; 349181421Sedwin /* Preserve av, copy spool servers to tmp_spool_buf. */ 350181421Sedwin strncpy(tmp_spool_buf, *av, strlen(*av)+1); 351181421Sedwin lsnat = 1; 352181421Sedwin } else 353181421Sedwin StrToAddr(*av, &r->laddr); 354181421Sedwin INC_ARGCV(); 355181421Sedwin 356181421Sedwin /* Extract public address. */ 357181421Sedwin if (ac == 0) 358181421Sedwin errx(EX_DATAERR, "redirect_addr: missing public address"); 359181421Sedwin StrToAddr(*av, &r->paddr); 360181421Sedwin INC_ARGCV(); 361181421Sedwin 362181421Sedwin /* Setup LSNAT server pool. */ 363163302Sru if (sep) { 364163302Sru sep = strtok(tmp_spool_buf, ","); 36593799Swollman while (sep != NULL) { 366163302Sru tmp = (struct cfg_spool *)spool_buf; 36767578Swollman if (len < SOF_SPOOL) 36893799Swollman goto nospace; 36914343Swollman len -= SOF_SPOOL; 37093799Swollman space += SOF_SPOOL; 37193799Swollman StrToAddr(sep, &tmp->addr); 37214343Swollman tmp->port = ~0; 37393799Swollman r->spool_cnt++; 374163302Sru /* Point to the next possible cfg_spool. */ 3752742Swollman spool_buf = &spool_buf[SOF_SPOOL]; 3762742Swollman sep = strtok(NULL, ","); 3772742Swollman } 37893799Swollman } 379163302Sru return(space); 380163302Srunospace: 381163302Sru errx(EX_DATAERR, "redirect_addr: buf is too small\n"); 382163302Sru} 38386222Swollman 38493799Swollmanstatic int 38514343Swollmansetup_redir_port(char *spool_buf, int len, 38693799Swollman int *_ac, char ***_av) 387163302Sru{ 388163302Sru char **av, *sep, *protoName; 389163302Sru char tmp_spool_buf[NAT_BUF_LEN]; 390163302Sru int ac, space, lsnat; 391163302Sru struct cfg_redir *r; 392163302Sru struct cfg_spool *tmp; 393163302Sru u_short numLocalPorts; 394163302Sru port_range portRange; 39567578Swollman 39619878Swollman av = *_av; 39714343Swollman ac = *_ac; 39893799Swollman space = 0; 399163302Sru lsnat = 0; 400163302Sru numLocalPorts = 0; 401163302Sru 402163302Sru if (len >= SOF_REDIR) { 403200835Sedwin r = (struct cfg_redir *)spool_buf; 404200835Sedwin /* Skip cfg_redir at beginning of buf. */ 405200835Sedwin spool_buf = &spool_buf[SOF_REDIR]; 406200835Sedwin space = SOF_REDIR; 407200835Sedwin len -= SOF_REDIR; 408200835Sedwin } else 409200835Sedwin goto nospace; 410200835Sedwin r->mode = REDIR_PORT; 411200835Sedwin /* 412200835Sedwin * Extract protocol. 413200835Sedwin */ 414200835Sedwin if (ac == 0) 415200835Sedwin errx (EX_DATAERR, "redirect_port: missing protocol"); 416200835Sedwin r->proto = StrToProto(*av); 417200835Sedwin protoName = *av; 418200835Sedwin INC_ARGCV(); 419200835Sedwin 420200835Sedwin /* 421200835Sedwin * Extract local address. 422200835Sedwin */ 423200835Sedwin if (ac == 0) 424200835Sedwin errx (EX_DATAERR, "redirect_port: missing local address"); 425200835Sedwin 426200835Sedwin sep = strchr(*av, ','); 427200835Sedwin /* LSNAT redirection syntax. */ 428200835Sedwin if (sep) { 429200835Sedwin r->laddr.s_addr = INADDR_NONE; 430200835Sedwin r->lport = ~0; 431200835Sedwin numLocalPorts = 1; 432200835Sedwin /* Preserve av, copy spool servers to tmp_spool_buf. */ 433200835Sedwin strncpy(tmp_spool_buf, *av, strlen(*av)+1); 434200835Sedwin lsnat = 1; 435200835Sedwin } else { 436200835Sedwin if (StrToAddrAndPortRange (*av, &r->laddr, protoName, 437200835Sedwin &portRange) != 0) 438200835Sedwin errx(EX_DATAERR, "redirect_port:" 439200835Sedwin "invalid local port range"); 440200835Sedwin 441200835Sedwin r->lport = GETLOPORT(portRange); 442200835Sedwin numLocalPorts = GETNUMPORTS(portRange); 443200835Sedwin } 444200835Sedwin INC_ARGCV(); 445200835Sedwin 446200835Sedwin /* 447200835Sedwin * Extract public port and optionally address. 448200835Sedwin */ 449200835Sedwin if (ac == 0) 450200835Sedwin errx (EX_DATAERR, "redirect_port: missing public port"); 451200835Sedwin 452200835Sedwin sep = strchr (*av, ':'); 453200835Sedwin if (sep) { 454200835Sedwin if (StrToAddrAndPortRange (*av, &r->paddr, protoName, 455200835Sedwin &portRange) != 0) 456200835Sedwin errx(EX_DATAERR, "redirect_port:" 457200835Sedwin "invalid public port range"); 458200835Sedwin } else { 459200835Sedwin r->paddr.s_addr = INADDR_ANY; 460200835Sedwin if (StrToPortRange (*av, protoName, &portRange) != 0) 461200835Sedwin errx(EX_DATAERR, "redirect_port:" 462200835Sedwin "invalid public port range"); 463200835Sedwin } 464200835Sedwin 465200835Sedwin r->pport = GETLOPORT(portRange); 46667578Swollman r->pport_cnt = GETNUMPORTS(portRange); 46719878Swollman INC_ARGCV(); 46819878Swollman 46914343Swollman /* 47093799Swollman * Extract remote address and optionally port. 471198825Sedwin */ 472198825Sedwin /* 473200835Sedwin * NB: isalpha(**av) => we've to check that next parameter is really an 474200835Sedwin * option for this redirect entry, else stop here processing arg[cv]. 475198825Sedwin */ 476198825Sedwin if (ac != 0 && !isalpha(**av)) { 477198825Sedwin sep = strchr (*av, ':'); 478198825Sedwin if (sep) { 479198825Sedwin if (StrToAddrAndPortRange (*av, &r->raddr, protoName, 480198825Sedwin &portRange) != 0) 481198825Sedwin errx(EX_DATAERR, "redirect_port:" 482198825Sedwin "invalid remote port range"); 483198825Sedwin } else { 484198825Sedwin SETLOPORT(portRange, 0); 485198825Sedwin SETNUMPORTS(portRange, 1); 486198825Sedwin StrToAddr (*av, &r->raddr); 487198825Sedwin } 488198825Sedwin INC_ARGCV(); 489198825Sedwin } else { 490198825Sedwin SETLOPORT(portRange, 0); 491198825Sedwin SETNUMPORTS(portRange, 1); 492198825Sedwin r->raddr.s_addr = INADDR_ANY; 493198825Sedwin } 494198825Sedwin r->rport = GETLOPORT(portRange); 495198825Sedwin r->rport_cnt = GETNUMPORTS(portRange); 496198825Sedwin 497198825Sedwin /* 498198825Sedwin * Make sure port ranges match up, then add the redirect ports. 499198825Sedwin */ 500198825Sedwin if (numLocalPorts != r->pport_cnt) 501198825Sedwin errx(EX_DATAERR, "redirect_port:" 502198825Sedwin "port ranges must be equal in size"); 503198825Sedwin 504198825Sedwin /* Remote port range is allowed to be '0' which means all ports. */ 505198825Sedwin if (r->rport_cnt != numLocalPorts && 506198825Sedwin (r->rport_cnt != 1 || r->rport != 0)) 507198825Sedwin errx(EX_DATAERR, "redirect_port: remote port must" 508198825Sedwin "be 0 or equal to local port range in size"); 509198825Sedwin 510198825Sedwin /* 511198825Sedwin * Setup LSNAT server pool. 512198825Sedwin */ 513198825Sedwin if (lsnat) { 514198825Sedwin sep = strtok(tmp_spool_buf, ","); 515198825Sedwin while (sep != NULL) { 516198825Sedwin tmp = (struct cfg_spool *)spool_buf; 517198825Sedwin if (len < SOF_SPOOL) 518198825Sedwin goto nospace; 519198825Sedwin len -= SOF_SPOOL; 520198825Sedwin space += SOF_SPOOL; 521198825Sedwin if (StrToAddrAndPortRange(sep, &tmp->addr, protoName, 522198825Sedwin &portRange) != 0) 523198825Sedwin errx(EX_DATAERR, "redirect_port:" 524198825Sedwin "invalid local port range"); 525198825Sedwin if (GETNUMPORTS(portRange) != 1) 526198825Sedwin errx(EX_DATAERR, "redirect_port: local port" 527198825Sedwin "must be single in this context"); 528198825Sedwin tmp->port = GETLOPORT(portRange); 529198825Sedwin r->spool_cnt++; 530198825Sedwin /* Point to the next possible cfg_spool. */ 531198825Sedwin spool_buf = &spool_buf[SOF_SPOOL]; 532198825Sedwin sep = strtok(NULL, ","); 533198825Sedwin } 534198825Sedwin } 535198825Sedwin return (space); 536198825Sedwinnospace: 537198825Sedwin errx(EX_DATAERR, "redirect_port: buf is too small\n"); 53867578Swollman} 5392742Swollman 540198825Sedwinstatic int 541198825Sedwinsetup_redir_proto(char *spool_buf, int len, 5422742Swollman int *_ac, char ***_av) 5432742Swollman{ 5442742Swollman char **av; 5452742Swollman int ac, space; 5462742Swollman struct protoent *protoent; 547198825Sedwin struct cfg_redir *r; 548198825Sedwin 5492742Swollman av = *_av; 5502742Swollman ac = *_ac; 5512742Swollman if (len >= SOF_REDIR) { 5522742Swollman r = (struct cfg_redir *)spool_buf; 5532742Swollman /* Skip cfg_redir at beginning of buf. */ 554213312Sedwin spool_buf = &spool_buf[SOF_REDIR]; 555213312Sedwin space = SOF_REDIR; 556198825Sedwin len -= SOF_REDIR; 557198825Sedwin } else 558198825Sedwin goto nospace; 5592742Swollman r->mode = REDIR_PROTO; 5602742Swollman /* 561198825Sedwin * Extract protocol. 562198825Sedwin */ 56358787Sru if (ac == 0) 5642742Swollman errx(EX_DATAERR, "redirect_proto: missing protocol"); 56530711Swollman 56630711Swollman protoent = getprotobyname(*av); 56743014Swollman if (protoent == NULL) 56830711Swollman errx(EX_DATAERR, "redirect_proto: unknown protocol %s", *av); 569158421Swollman else 57043543Swollman r->proto = protoent->p_proto; 57143543Swollman 57243543Swollman INC_ARGCV(); 573206868Sedwin 574206868Sedwin /* 575206868Sedwin * Extract local address. 576206868Sedwin */ 577206868Sedwin if (ac == 0) 578206868Sedwin errx(EX_DATAERR, "redirect_proto: missing local address"); 579206868Sedwin else 580206868Sedwin StrToAddr(*av, &r->laddr); 581206868Sedwin 582206868Sedwin INC_ARGCV(); 583206868Sedwin 584206868Sedwin /* 585206868Sedwin * Extract optional public address. 586206868Sedwin */ 587206868Sedwin if (ac == 0) { 588206868Sedwin r->paddr.s_addr = INADDR_ANY; 589206868Sedwin r->raddr.s_addr = INADDR_ANY; 590206868Sedwin } else { 591206868Sedwin /* see above in setup_redir_port() */ 592206868Sedwin if (!isalpha(**av)) { 593206868Sedwin StrToAddr(*av, &r->paddr); 594206868Sedwin INC_ARGCV(); 59530711Swollman 59630711Swollman /* 59730711Swollman * Extract optional remote address. 59830711Swollman */ 59930711Swollman /* see above in setup_redir_port() */ 60030711Swollman if (ac!=0 && !isalpha(**av)) { 60130711Swollman StrToAddr(*av, &r->raddr); 60230711Swollman INC_ARGCV(); 60330711Swollman } 60430711Swollman } 605206868Sedwin } 606206868Sedwin return (space); 607206868Sedwinnospace: 60830711Swollman errx(EX_DATAERR, "redirect_proto: buf is too small\n"); 60986222Swollman} 61030711Swollman 61130711Swollmanstatic void 61293799Swollmanprint_nat_config(unsigned char *buf) 6132742Swollman{ 61493799Swollman struct cfg_nat *n; 61593799Swollman int i, cnt, flag, off; 61693799Swollman struct cfg_redir *t; 61793799Swollman struct cfg_spool *s; 61893799Swollman struct protoent *p; 61993799Swollman 62093799Swollman n = (struct cfg_nat *)buf; 62193799Swollman flag = 1; 62293799Swollman off = sizeof(*n); 62393799Swollman printf("ipfw nat %u config", n->id); 62493799Swollman if (strlen(n->if_name) != 0) 62593799Swollman printf(" if %s", n->if_name); 62693799Swollman else if (n->ip.s_addr != 0) 62793799Swollman printf(" ip %s", inet_ntoa(n->ip)); 6282742Swollman while (n->mode != 0) { 62993799Swollman if (n->mode & PKT_ALIAS_LOG) { 63093799Swollman printf(" log"); 63119878Swollman n->mode &= ~PKT_ALIAS_LOG; 6322742Swollman } else if (n->mode & PKT_ALIAS_DENY_INCOMING) { 6332742Swollman printf(" deny_in"); 6342742Swollman n->mode &= ~PKT_ALIAS_DENY_INCOMING; 6352742Swollman } else if (n->mode & PKT_ALIAS_SAME_PORTS) { 6362742Swollman printf(" same_ports"); 6372742Swollman n->mode &= ~PKT_ALIAS_SAME_PORTS; 63819878Swollman } else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) { 6392742Swollman printf(" unreg_only"); 64019878Swollman n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY; 6412742Swollman } else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) { 64219878Swollman printf(" reset"); 6432742Swollman n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE; 6442742Swollman } else if (n->mode & PKT_ALIAS_REVERSE) { 64543543Swollman printf(" reverse"); 64643543Swollman n->mode &= ~PKT_ALIAS_REVERSE; 6472742Swollman } else if (n->mode & PKT_ALIAS_PROXY_ONLY) { 6482742Swollman printf(" proxy_only"); 64943543Swollman n->mode &= ~PKT_ALIAS_PROXY_ONLY; 65058787Sru } 65143543Swollman } 6522742Swollman /* Print all the redirect's data configuration. */ 65367578Swollman for (cnt = 0; cnt < n->redir_cnt; cnt++) { 65467578Swollman t = (struct cfg_redir *)&buf[off]; 65567578Swollman off += SOF_REDIR; 65667578Swollman switch (t->mode) { 6572742Swollman case REDIR_ADDR: 658149514Swollman printf(" redirect_addr"); 6599908Swollman if (t->spool_cnt == 0) 6609908Swollman printf(" %s", inet_ntoa(t->laddr)); 6619908Swollman else 66214343Swollman for (i = 0; i < t->spool_cnt; i++) { 66314343Swollman s = (struct cfg_spool *)&buf[off]; 664149514Swollman if (i) 66520094Swollman printf(","); 66620094Swollman else 66720094Swollman printf(" "); 668136638Swollman printf("%s", inet_ntoa(s->addr)); 669136638Swollman off += SOF_SPOOL; 670149514Swollman } 671136638Swollman printf(" %s", inet_ntoa(t->paddr)); 672136638Swollman break; 673136638Swollman case REDIR_PORT: 674136638Swollman p = getprotobynumber(t->proto); 675136638Swollman printf(" redirect_port %s ", p->p_name); 676136638Swollman if (!t->spool_cnt) { 677136638Swollman printf("%s:%u", inet_ntoa(t->laddr), t->lport); 678153670Swollman if (t->pport_cnt > 1) 679153670Swollman printf("-%u", t->lport + 680153670Swollman t->pport_cnt - 1); 681153670Swollman } else 682153670Swollman for (i=0; i < t->spool_cnt; i++) { 683153670Swollman s = (struct cfg_spool *)&buf[off]; 684153670Swollman if (i) 685153670Swollman printf(","); 686153670Swollman printf("%s:%u", inet_ntoa(s->addr), 687153670Swollman s->port); 688153670Swollman off += SOF_SPOOL; 6892742Swollman } 6902742Swollman 69119878Swollman printf(" "); 69219878Swollman if (t->paddr.s_addr) 69319878Swollman printf("%s:", inet_ntoa(t->paddr)); 69419878Swollman printf("%u", t->pport); 69520094Swollman if (!t->spool_cnt && t->pport_cnt > 1) 69620094Swollman printf("-%u", t->pport + t->pport_cnt - 1); 69720094Swollman 69843543Swollman if (t->raddr.s_addr) { 699136638Swollman printf(" %s", inet_ntoa(t->raddr)); 700153670Swollman if (t->rport) { 701153670Swollman printf(":%u", t->rport); 7022742Swollman if (!t->spool_cnt && t->rport_cnt > 1) 70358787Sru printf("-%u", t->rport + 70475267Swollman t->rport_cnt - 1); 705169811Swollman } 706169811Swollman } 70775267Swollman break; 70875267Swollman case REDIR_PROTO: 70975267Swollman p = getprotobynumber(t->proto); 71075267Swollman printf(" redirect_proto %s %s", p->p_name, 71175267Swollman inet_ntoa(t->laddr)); 71275267Swollman if (t->paddr.s_addr != 0) { 71375267Swollman printf(" %s", inet_ntoa(t->paddr)); 71475267Swollman if (t->raddr.s_addr) 71575267Swollman printf(" %s", inet_ntoa(t->raddr)); 71675267Swollman } 71775267Swollman break; 71875267Swollman default: 71975267Swollman errx(EX_DATAERR, "unknown redir mode"); 72075267Swollman break; 72175267Swollman } 72275267Swollman } 72375267Swollman printf("\n"); 72475267Swollman} 72575267Swollman 72675267Swollmanvoid 72775267Swollmanipfw_config_nat(int ac, char **av) 72858787Sru{ 72958787Sru struct cfg_nat *n; /* Nat instance configuration. */ 730149514Swollman int i, len, off, tok; 731169811Swollman char *id, buf[NAT_BUF_LEN]; /* Buffer for serialized data. */ 732149514Swollman 73386222Swollman len = NAT_BUF_LEN; 734149514Swollman /* Offset in buf: save space for n at the beginning. */ 73558787Sru off = sizeof(*n); 7362742Swollman memset(buf, 0, sizeof(buf)); 7372742Swollman n = (struct cfg_nat *)buf; 738177591Sedwin 73919878Swollman av++; ac--; 74019878Swollman /* Nat id. */ 7412742Swollman if (ac && isdigit(**av)) { 7422742Swollman id = *av; 7432742Swollman i = atoi(*av); 744177591Sedwin ac--; av++; 7452742Swollman n->id = i; 7462742Swollman } else 7472742Swollman errx(EX_DATAERR, "missing nat id"); 7482742Swollman if (ac == 0) 7492742Swollman errx(EX_DATAERR, "missing option"); 75086222Swollman 751158421Swollman while (ac > 0) { 75286222Swollman tok = match_token(nat_params, *av); 75386222Swollman ac--; av++; 75486222Swollman switch (tok) { 75586222Swollman case TOK_IP: 75686222Swollman if (ac == 0) 757169811Swollman errx(EX_DATAERR, "missing option"); 758169811Swollman if (!inet_aton(av[0], &(n->ip))) 759169811Swollman errx(EX_DATAERR, "bad ip address ``%s''", 760169811Swollman av[0]); 761169811Swollman ac--; av++; 762169811Swollman break; 763169811Swollman case TOK_IF: 764169811Swollman if (ac == 0) 765169811Swollman errx(EX_DATAERR, "missing option"); 766169811Swollman set_addr_dynamic(av[0], n); 767169811Swollman ac--; av++; 768169811Swollman break; 769169811Swollman case TOK_ALOG: 7702742Swollman n->mode |= PKT_ALIAS_LOG; 7712742Swollman break; 772158421Swollman case TOK_DENY_INC: 77358787Sru n->mode |= PKT_ALIAS_DENY_INCOMING; 77458787Sru break; 77519878Swollman case TOK_SAME_PORTS: 77686222Swollman n->mode |= PKT_ALIAS_SAME_PORTS; 777169811Swollman break; 77886222Swollman case TOK_UNREG_ONLY: 77986222Swollman n->mode |= PKT_ALIAS_UNREGISTERED_ONLY; 78086222Swollman break; 78186222Swollman case TOK_RESET_ADDR: 78286222Swollman n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; 78386222Swollman break; 78486222Swollman case TOK_ALIAS_REV: 785169811Swollman n->mode |= PKT_ALIAS_REVERSE; 78686222Swollman break; 78786222Swollman case TOK_PROXY_ONLY: 78886222Swollman n->mode |= PKT_ALIAS_PROXY_ONLY; 78986222Swollman break; 79086222Swollman /* 79193799Swollman * All the setup_redir_* functions work directly in the final 79219878Swollman * buffer, see above for details. 79386222Swollman */ 794169811Swollman case TOK_REDIR_ADDR: 79586222Swollman case TOK_REDIR_PORT: 7962742Swollman case TOK_REDIR_PROTO: 797169811Swollman switch (tok) { 7982742Swollman case TOK_REDIR_ADDR: 79986222Swollman i = setup_redir_addr(&buf[off], len, &ac, &av); 8002742Swollman break; 8012742Swollman case TOK_REDIR_PORT: 802114173Swollman i = setup_redir_port(&buf[off], len, &ac, &av); 803114173Swollman break; 804114173Swollman case TOK_REDIR_PROTO: 805114173Swollman i = setup_redir_proto(&buf[off], len, &ac, &av); 80621217Swollman break; 807114173Swollman } 808114173Swollman n->redir_cnt++; 80921217Swollman off += i; 810114173Swollman len -= i; 811114173Swollman break; 812114173Swollman default: 813114173Swollman errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 814114173Swollman } 815114173Swollman } 816114173Swollman 817114173Swollman i = do_cmd(IP_FW_NAT_CFG, buf, off); 818114173Swollman if (i) 819114173Swollman err(1, "setsockopt(%s)", "IP_FW_NAT_CFG"); 820114173Swollman 821114173Swollman if (!co.do_quiet) { 822114173Swollman /* After every modification, we show the resultant rule. */ 823114173Swollman int _ac = 3; 824114173Swollman char *_av[] = {"show", "config", id}; 825114173Swollman ipfw_show_nat(_ac, _av); 826114173Swollman } 827114173Swollman} 828114173Swollman 829114173Swollman 830114173Swollmanvoid 831114173Swollmanipfw_show_nat(int ac, char **av) 832149514Swollman{ 833149514Swollman struct cfg_nat *n; 834149514Swollman struct cfg_redir *e; 835149514Swollman int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size; 836149514Swollman int nat_cnt, redir_cnt, r; 837149514Swollman uint8_t *data, *p; 838149514Swollman char *endptr; 839158421Swollman 840158421Swollman do_rule = 0; 841149514Swollman nalloc = 1024; 842149514Swollman size = 0; 843149514Swollman data = NULL; 844149514Swollman frule = 0; 84521217Swollman lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */ 846149514Swollman ac--; av++; 847149514Swollman 848149514Swollman if (co.test_only) 849149514Swollman return; 850149514Swollman 851149514Swollman /* Parse parameters. */ 852149514Swollman for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) { 853149514Swollman if (!strncmp(av[0], "config", strlen(av[0]))) { 854149514Swollman cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1; 855149514Swollman continue; 856149514Swollman } 857149514Swollman /* Convert command line rule #. */ 858149514Swollman frule = lrule = strtoul(av[0], &endptr, 10); 859149514Swollman if (*endptr == '-') 860158421Swollman lrule = strtoul(endptr+1, &endptr, 10); 861158421Swollman if (lrule == 0) 862158421Swollman err(EX_USAGE, "invalid rule number: %s", av[0]); 863158421Swollman do_rule = 1; 864172479Sedwin } 865172479Sedwin 866172479Sedwin nbytes = nalloc; 867172479Sedwin while (nbytes >= nalloc) { 868172479Sedwin nalloc = nalloc * 2; 869174242Sedwin nbytes = nalloc; 870174242Sedwin data = safe_realloc(data, nbytes); 871174242Sedwin if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0) 872174242Sedwin err(EX_OSERR, "getsockopt(IP_FW_GET_%s)", 873174242Sedwin (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG"); 874174242Sedwin } 875174242Sedwin if (nbytes == 0) 876174242Sedwin exit(0); 877174242Sedwin if (do_cfg) { 8782742Swollman nat_cnt = *((int *)data); 879114173Swollman for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) { 880114173Swollman n = (struct cfg_nat *)&data[i]; 881114173Swollman if (frule <= n->id && lrule >= n->id) 882114173Swollman print_nat_config(&data[i]); 883114173Swollman i += sizeof(struct cfg_nat); 884114173Swollman for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) { 885114173Swollman e = (struct cfg_redir *)&data[i]; 886114173Swollman i += sizeof(struct cfg_redir) + e->spool_cnt * 887114173Swollman sizeof(struct cfg_spool); 888114173Swollman } 889114173Swollman } 890114173Swollman } else { 891114173Swollman for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) { 892114173Swollman p = &data[i]; 893114173Swollman if (p == data + nbytes) 894114173Swollman break; 895114173Swollman bcopy(p, &r, sizeof(int)); 896158421Swollman if (do_rule) { 897158421Swollman if (!(frule <= r && lrule >= r)) 898172479Sedwin continue; 899172479Sedwin } 900172479Sedwin printf("nat %u: %s\n", r, p+sizeof(int)); 901172479Sedwin } 902172479Sedwin } 903172479Sedwin} 904172479Sedwin