1 |
/* $MidnightBSD$ */ |
2 |
/* |
3 |
* Copyright (c) 2002-2003 Luigi Rizzo |
4 |
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp |
5 |
* Copyright (c) 1994 Ugen J.S.Antsilevich |
6 |
* |
7 |
* Idea and grammar partially left from: |
8 |
* Copyright (c) 1993 Daniel Boulet |
9 |
* |
10 |
* Redistribution and use in source forms, with and without modification, |
11 |
* are permitted provided that this entire comment appears intact. |
12 |
* |
13 |
* Redistribution in binary form may occur without any restrictions. |
14 |
* Obviously, it would be nice if you gave credit where credit is due |
15 |
* but requiring it would be too onerous. |
16 |
* |
17 |
* This software is provided ``AS IS'' without any warranties of any kind. |
18 |
* |
19 |
* NEW command line interface for IP firewall facility |
20 |
* |
21 |
* $FreeBSD: stable/10/sbin/ipfw/nat.c 278324 2015-02-06 18:13:29Z jhb $ |
22 |
* |
23 |
* In-kernel nat support |
24 |
*/ |
25 |
|
26 |
#include <sys/types.h> |
27 |
#include <sys/socket.h> |
28 |
#include <sys/sysctl.h> |
29 |
|
30 |
#include "ipfw2.h" |
31 |
|
32 |
#include <ctype.h> |
33 |
#include <err.h> |
34 |
#include <netdb.h> |
35 |
#include <stdio.h> |
36 |
#include <stdlib.h> |
37 |
#include <string.h> |
38 |
#include <sysexits.h> |
39 |
|
40 |
#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */ |
41 |
|
42 |
#include <net/if.h> |
43 |
#include <net/if_dl.h> |
44 |
#include <net/route.h> /* def. of struct route */ |
45 |
#include <netinet/in.h> |
46 |
#include <netinet/ip_fw.h> |
47 |
#include <arpa/inet.h> |
48 |
#include <alias.h> |
49 |
|
50 |
static struct _s_x nat_params[] = { |
51 |
{ "ip", TOK_IP }, |
52 |
{ "if", TOK_IF }, |
53 |
{ "log", TOK_ALOG }, |
54 |
{ "deny_in", TOK_DENY_INC }, |
55 |
{ "same_ports", TOK_SAME_PORTS }, |
56 |
{ "unreg_only", TOK_UNREG_ONLY }, |
57 |
{ "skip_global", TOK_SKIP_GLOBAL }, |
58 |
{ "reset", TOK_RESET_ADDR }, |
59 |
{ "reverse", TOK_ALIAS_REV }, |
60 |
{ "proxy_only", TOK_PROXY_ONLY }, |
61 |
{ "redirect_addr", TOK_REDIR_ADDR }, |
62 |
{ "redirect_port", TOK_REDIR_PORT }, |
63 |
{ "redirect_proto", TOK_REDIR_PROTO }, |
64 |
{ NULL, 0 } /* terminator */ |
65 |
}; |
66 |
|
67 |
|
68 |
/* |
69 |
* Search for interface with name "ifn", and fill n accordingly: |
70 |
* |
71 |
* n->ip ip address of interface "ifn" |
72 |
* n->if_name copy of interface name "ifn" |
73 |
*/ |
74 |
static void |
75 |
set_addr_dynamic(const char *ifn, struct cfg_nat *n) |
76 |
{ |
77 |
size_t needed; |
78 |
int mib[6]; |
79 |
char *buf, *lim, *next; |
80 |
struct if_msghdr *ifm; |
81 |
struct ifa_msghdr *ifam; |
82 |
struct sockaddr_dl *sdl; |
83 |
struct sockaddr_in *sin; |
84 |
int ifIndex, ifMTU; |
85 |
|
86 |
mib[0] = CTL_NET; |
87 |
mib[1] = PF_ROUTE; |
88 |
mib[2] = 0; |
89 |
mib[3] = AF_INET; |
90 |
mib[4] = NET_RT_IFLIST; |
91 |
mib[5] = 0; |
92 |
/* |
93 |
* Get interface data. |
94 |
*/ |
95 |
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) |
96 |
err(1, "iflist-sysctl-estimate"); |
97 |
buf = safe_calloc(1, needed); |
98 |
if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) |
99 |
err(1, "iflist-sysctl-get"); |
100 |
lim = buf + needed; |
101 |
/* |
102 |
* Loop through interfaces until one with |
103 |
* given name is found. This is done to |
104 |
* find correct interface index for routing |
105 |
* message processing. |
106 |
*/ |
107 |
ifIndex = 0; |
108 |
next = buf; |
109 |
while (next < lim) { |
110 |
ifm = (struct if_msghdr *)next; |
111 |
next += ifm->ifm_msglen; |
112 |
if (ifm->ifm_version != RTM_VERSION) { |
113 |
if (co.verbose) |
114 |
warnx("routing message version %d " |
115 |
"not understood", ifm->ifm_version); |
116 |
continue; |
117 |
} |
118 |
if (ifm->ifm_type == RTM_IFINFO) { |
119 |
sdl = (struct sockaddr_dl *)(ifm + 1); |
120 |
if (strlen(ifn) == sdl->sdl_nlen && |
121 |
strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { |
122 |
ifIndex = ifm->ifm_index; |
123 |
ifMTU = ifm->ifm_data.ifi_mtu; |
124 |
break; |
125 |
} |
126 |
} |
127 |
} |
128 |
if (!ifIndex) |
129 |
errx(1, "unknown interface name %s", ifn); |
130 |
/* |
131 |
* Get interface address. |
132 |
*/ |
133 |
sin = NULL; |
134 |
while (next < lim) { |
135 |
ifam = (struct ifa_msghdr *)next; |
136 |
next += ifam->ifam_msglen; |
137 |
if (ifam->ifam_version != RTM_VERSION) { |
138 |
if (co.verbose) |
139 |
warnx("routing message version %d " |
140 |
"not understood", ifam->ifam_version); |
141 |
continue; |
142 |
} |
143 |
if (ifam->ifam_type != RTM_NEWADDR) |
144 |
break; |
145 |
if (ifam->ifam_addrs & RTA_IFA) { |
146 |
int i; |
147 |
char *cp = (char *)(ifam + 1); |
148 |
|
149 |
for (i = 1; i < RTA_IFA; i <<= 1) { |
150 |
if (ifam->ifam_addrs & i) |
151 |
cp += SA_SIZE((struct sockaddr *)cp); |
152 |
} |
153 |
if (((struct sockaddr *)cp)->sa_family == AF_INET) { |
154 |
sin = (struct sockaddr_in *)cp; |
155 |
break; |
156 |
} |
157 |
} |
158 |
} |
159 |
if (sin == NULL) |
160 |
n->ip.s_addr = htonl(INADDR_ANY); |
161 |
else |
162 |
n->ip = sin->sin_addr; |
163 |
strncpy(n->if_name, ifn, IF_NAMESIZE); |
164 |
|
165 |
free(buf); |
166 |
} |
167 |
|
168 |
/* |
169 |
* XXX - The following functions, macros and definitions come from natd.c: |
170 |
* it would be better to move them outside natd.c, in a file |
171 |
* (redirect_support.[ch]?) shared by ipfw and natd, but for now i can live |
172 |
* with it. |
173 |
*/ |
174 |
|
175 |
/* |
176 |
* Definition of a port range, and macros to deal with values. |
177 |
* FORMAT: HI 16-bits == first port in range, 0 == all ports. |
178 |
* LO 16-bits == number of ports in range |
179 |
* NOTES: - Port values are not stored in network byte order. |
180 |
*/ |
181 |
|
182 |
#define port_range u_long |
183 |
|
184 |
#define GETLOPORT(x) ((x) >> 0x10) |
185 |
#define GETNUMPORTS(x) ((x) & 0x0000ffff) |
186 |
#define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x))) |
187 |
|
188 |
/* Set y to be the low-port value in port_range variable x. */ |
189 |
#define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10)) |
190 |
|
191 |
/* Set y to be the number of ports in port_range variable x. */ |
192 |
#define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y)) |
193 |
|
194 |
static void |
195 |
StrToAddr (const char* str, struct in_addr* addr) |
196 |
{ |
197 |
struct hostent* hp; |
198 |
|
199 |
if (inet_aton (str, addr)) |
200 |
return; |
201 |
|
202 |
hp = gethostbyname (str); |
203 |
if (!hp) |
204 |
errx (1, "unknown host %s", str); |
205 |
|
206 |
memcpy (addr, hp->h_addr, sizeof (struct in_addr)); |
207 |
} |
208 |
|
209 |
static int |
210 |
StrToPortRange (const char* str, const char* proto, port_range *portRange) |
211 |
{ |
212 |
char* sep; |
213 |
struct servent* sp; |
214 |
char* end; |
215 |
u_short loPort; |
216 |
u_short hiPort; |
217 |
|
218 |
/* First see if this is a service, return corresponding port if so. */ |
219 |
sp = getservbyname (str,proto); |
220 |
if (sp) { |
221 |
SETLOPORT(*portRange, ntohs(sp->s_port)); |
222 |
SETNUMPORTS(*portRange, 1); |
223 |
return 0; |
224 |
} |
225 |
|
226 |
/* Not a service, see if it's a single port or port range. */ |
227 |
sep = strchr (str, '-'); |
228 |
if (sep == NULL) { |
229 |
SETLOPORT(*portRange, strtol(str, &end, 10)); |
230 |
if (end != str) { |
231 |
/* Single port. */ |
232 |
SETNUMPORTS(*portRange, 1); |
233 |
return 0; |
234 |
} |
235 |
|
236 |
/* Error in port range field. */ |
237 |
errx (EX_DATAERR, "%s/%s: unknown service", str, proto); |
238 |
} |
239 |
|
240 |
/* Port range, get the values and sanity check. */ |
241 |
sscanf (str, "%hu-%hu", &loPort, &hiPort); |
242 |
SETLOPORT(*portRange, loPort); |
243 |
SETNUMPORTS(*portRange, 0); /* Error by default */ |
244 |
if (loPort <= hiPort) |
245 |
SETNUMPORTS(*portRange, hiPort - loPort + 1); |
246 |
|
247 |
if (GETNUMPORTS(*portRange) == 0) |
248 |
errx (EX_DATAERR, "invalid port range %s", str); |
249 |
|
250 |
return 0; |
251 |
} |
252 |
|
253 |
static int |
254 |
StrToProto (const char* str) |
255 |
{ |
256 |
if (!strcmp (str, "tcp")) |
257 |
return IPPROTO_TCP; |
258 |
|
259 |
if (!strcmp (str, "udp")) |
260 |
return IPPROTO_UDP; |
261 |
|
262 |
if (!strcmp (str, "sctp")) |
263 |
return IPPROTO_SCTP; |
264 |
errx (EX_DATAERR, "unknown protocol %s. Expected sctp, tcp or udp", str); |
265 |
} |
266 |
|
267 |
static int |
268 |
StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, |
269 |
port_range *portRange) |
270 |
{ |
271 |
char* ptr; |
272 |
|
273 |
ptr = strchr (str, ':'); |
274 |
if (!ptr) |
275 |
errx (EX_DATAERR, "%s is missing port number", str); |
276 |
|
277 |
*ptr = '\0'; |
278 |
++ptr; |
279 |
|
280 |
StrToAddr (str, addr); |
281 |
return StrToPortRange (ptr, proto, portRange); |
282 |
} |
283 |
|
284 |
/* End of stuff taken from natd.c. */ |
285 |
|
286 |
/* |
287 |
* The next 3 functions add support for the addr, port and proto redirect and |
288 |
* their logic is loosely based on SetupAddressRedirect(), SetupPortRedirect() |
289 |
* and SetupProtoRedirect() from natd.c. |
290 |
* |
291 |
* Every setup_* function fills at least one redirect entry |
292 |
* (struct cfg_redir) and zero or more server pool entry (struct cfg_spool) |
293 |
* in buf. |
294 |
* |
295 |
* The format of data in buf is: |
296 |
* |
297 |
* cfg_nat cfg_redir cfg_spool ...... cfg_spool |
298 |
* |
299 |
* ------------------------------------- ------------ |
300 |
* | | .....X ... | | | | ..... |
301 |
* ------------------------------------- ...... ------------ |
302 |
* ^ |
303 |
* spool_cnt n=0 ...... n=(X-1) |
304 |
* |
305 |
* len points to the amount of available space in buf |
306 |
* space counts the memory consumed by every function |
307 |
* |
308 |
* XXX - Every function get all the argv params so it |
309 |
* has to check, in optional parameters, that the next |
310 |
* args is a valid option for the redir entry and not |
311 |
* another token. Only redir_port and redir_proto are |
312 |
* affected by this. |
313 |
*/ |
314 |
|
315 |
static int |
316 |
estimate_redir_addr(int *ac, char ***av) |
317 |
{ |
318 |
size_t space = sizeof(struct cfg_redir); |
319 |
char *sep = **av; |
320 |
u_int c = 0; |
321 |
|
322 |
(void)ac; /* UNUSED */ |
323 |
while ((sep = strchr(sep, ',')) != NULL) { |
324 |
c++; |
325 |
sep++; |
326 |
} |
327 |
|
328 |
if (c > 0) |
329 |
c++; |
330 |
|
331 |
space += c * sizeof(struct cfg_spool); |
332 |
|
333 |
return (space); |
334 |
} |
335 |
|
336 |
static int |
337 |
setup_redir_addr(char *buf, int *ac, char ***av) |
338 |
{ |
339 |
struct cfg_redir *r; |
340 |
char *sep; |
341 |
size_t space; |
342 |
|
343 |
r = (struct cfg_redir *)buf; |
344 |
r->mode = REDIR_ADDR; |
345 |
/* Skip cfg_redir at beginning of buf. */ |
346 |
buf = &buf[sizeof(struct cfg_redir)]; |
347 |
space = sizeof(struct cfg_redir); |
348 |
|
349 |
/* Extract local address. */ |
350 |
if (strchr(**av, ',') != NULL) { |
351 |
struct cfg_spool *spool; |
352 |
|
353 |
/* Setup LSNAT server pool. */ |
354 |
r->laddr.s_addr = INADDR_NONE; |
355 |
sep = strtok(**av, ","); |
356 |
while (sep != NULL) { |
357 |
spool = (struct cfg_spool *)buf; |
358 |
space += sizeof(struct cfg_spool); |
359 |
StrToAddr(sep, &spool->addr); |
360 |
spool->port = ~0; |
361 |
r->spool_cnt++; |
362 |
/* Point to the next possible cfg_spool. */ |
363 |
buf = &buf[sizeof(struct cfg_spool)]; |
364 |
sep = strtok(NULL, ","); |
365 |
} |
366 |
} else |
367 |
StrToAddr(**av, &r->laddr); |
368 |
(*av)++; (*ac)--; |
369 |
|
370 |
/* Extract public address. */ |
371 |
StrToAddr(**av, &r->paddr); |
372 |
(*av)++; (*ac)--; |
373 |
|
374 |
return (space); |
375 |
} |
376 |
|
377 |
static int |
378 |
estimate_redir_port(int *ac, char ***av) |
379 |
{ |
380 |
size_t space = sizeof(struct cfg_redir); |
381 |
char *sep = **av; |
382 |
u_int c = 0; |
383 |
|
384 |
(void)ac; /* UNUSED */ |
385 |
while ((sep = strchr(sep, ',')) != NULL) { |
386 |
c++; |
387 |
sep++; |
388 |
} |
389 |
|
390 |
if (c > 0) |
391 |
c++; |
392 |
|
393 |
space += c * sizeof(struct cfg_spool); |
394 |
|
395 |
return (space); |
396 |
} |
397 |
|
398 |
static int |
399 |
setup_redir_port(char *buf, int *ac, char ***av) |
400 |
{ |
401 |
struct cfg_redir *r; |
402 |
char *sep, *protoName, *lsnat = NULL; |
403 |
size_t space; |
404 |
u_short numLocalPorts; |
405 |
port_range portRange; |
406 |
|
407 |
numLocalPorts = 0; |
408 |
|
409 |
r = (struct cfg_redir *)buf; |
410 |
r->mode = REDIR_PORT; |
411 |
/* Skip cfg_redir at beginning of buf. */ |
412 |
buf = &buf[sizeof(struct cfg_redir)]; |
413 |
space = sizeof(struct cfg_redir); |
414 |
|
415 |
/* |
416 |
* Extract protocol. |
417 |
*/ |
418 |
r->proto = StrToProto(**av); |
419 |
protoName = **av; |
420 |
(*av)++; (*ac)--; |
421 |
|
422 |
/* |
423 |
* Extract local address. |
424 |
*/ |
425 |
if (strchr(**av, ',') != NULL) { |
426 |
r->laddr.s_addr = INADDR_NONE; |
427 |
r->lport = ~0; |
428 |
numLocalPorts = 1; |
429 |
lsnat = **av; |
430 |
} else { |
431 |
/* |
432 |
* The sctp nat does not allow the port numbers to be mapped to |
433 |
* new port numbers. Therefore, no ports are to be specified |
434 |
* in the target port field. |
435 |
*/ |
436 |
if (r->proto == IPPROTO_SCTP) { |
437 |
if (strchr(**av, ':')) |
438 |
errx(EX_DATAERR, "redirect_port:" |
439 |
"port numbers do not change in sctp, so do " |
440 |
"not specify them as part of the target"); |
441 |
else |
442 |
StrToAddr(**av, &r->laddr); |
443 |
} else { |
444 |
if (StrToAddrAndPortRange(**av, &r->laddr, protoName, |
445 |
&portRange) != 0) |
446 |
errx(EX_DATAERR, "redirect_port: " |
447 |
"invalid local port range"); |
448 |
|
449 |
r->lport = GETLOPORT(portRange); |
450 |
numLocalPorts = GETNUMPORTS(portRange); |
451 |
} |
452 |
} |
453 |
(*av)++; (*ac)--; |
454 |
|
455 |
/* |
456 |
* Extract public port and optionally address. |
457 |
*/ |
458 |
if (strchr(**av, ':') != NULL) { |
459 |
if (StrToAddrAndPortRange(**av, &r->paddr, protoName, |
460 |
&portRange) != 0) |
461 |
errx(EX_DATAERR, "redirect_port: " |
462 |
"invalid public port range"); |
463 |
} else { |
464 |
r->paddr.s_addr = INADDR_ANY; |
465 |
if (StrToPortRange(**av, protoName, &portRange) != 0) |
466 |
errx(EX_DATAERR, "redirect_port: " |
467 |
"invalid public port range"); |
468 |
} |
469 |
|
470 |
r->pport = GETLOPORT(portRange); |
471 |
if (r->proto == IPPROTO_SCTP) { /* so the logic below still works */ |
472 |
numLocalPorts = GETNUMPORTS(portRange); |
473 |
r->lport = r->pport; |
474 |
} |
475 |
r->pport_cnt = GETNUMPORTS(portRange); |
476 |
(*av)++; (*ac)--; |
477 |
|
478 |
/* |
479 |
* Extract remote address and optionally port. |
480 |
*/ |
481 |
/* |
482 |
* NB: isdigit(**av) => we've to check that next parameter is really an |
483 |
* option for this redirect entry, else stop here processing arg[cv]. |
484 |
*/ |
485 |
if (*ac != 0 && isdigit(***av)) { |
486 |
if (strchr(**av, ':') != NULL) { |
487 |
if (StrToAddrAndPortRange(**av, &r->raddr, protoName, |
488 |
&portRange) != 0) |
489 |
errx(EX_DATAERR, "redirect_port: " |
490 |
"invalid remote port range"); |
491 |
} else { |
492 |
SETLOPORT(portRange, 0); |
493 |
SETNUMPORTS(portRange, 1); |
494 |
StrToAddr(**av, &r->raddr); |
495 |
} |
496 |
(*av)++; (*ac)--; |
497 |
} else { |
498 |
SETLOPORT(portRange, 0); |
499 |
SETNUMPORTS(portRange, 1); |
500 |
r->raddr.s_addr = INADDR_ANY; |
501 |
} |
502 |
r->rport = GETLOPORT(portRange); |
503 |
r->rport_cnt = GETNUMPORTS(portRange); |
504 |
|
505 |
/* |
506 |
* Make sure port ranges match up, then add the redirect ports. |
507 |
*/ |
508 |
if (numLocalPorts != r->pport_cnt) |
509 |
errx(EX_DATAERR, "redirect_port: " |
510 |
"port ranges must be equal in size"); |
511 |
|
512 |
/* Remote port range is allowed to be '0' which means all ports. */ |
513 |
if (r->rport_cnt != numLocalPorts && |
514 |
(r->rport_cnt != 1 || r->rport != 0)) |
515 |
errx(EX_DATAERR, "redirect_port: remote port must" |
516 |
"be 0 or equal to local port range in size"); |
517 |
|
518 |
/* Setup LSNAT server pool. */ |
519 |
if (lsnat != NULL) { |
520 |
struct cfg_spool *spool; |
521 |
|
522 |
sep = strtok(lsnat, ","); |
523 |
while (sep != NULL) { |
524 |
spool = (struct cfg_spool *)buf; |
525 |
space += sizeof(struct cfg_spool); |
526 |
/* |
527 |
* The sctp nat does not allow the port numbers to |
528 |
* be mapped to new port numbers. Therefore, no ports |
529 |
* are to be specified in the target port field. |
530 |
*/ |
531 |
if (r->proto == IPPROTO_SCTP) { |
532 |
if (strchr (sep, ':')) { |
533 |
errx(EX_DATAERR, "redirect_port:" |
534 |
"port numbers do not change in " |
535 |
"sctp, so do not specify them as " |
536 |
"part of the target"); |
537 |
} else { |
538 |
StrToAddr(sep, &spool->addr); |
539 |
spool->port = r->pport; |
540 |
} |
541 |
} else { |
542 |
if (StrToAddrAndPortRange(sep, &spool->addr, |
543 |
protoName, &portRange) != 0) |
544 |
errx(EX_DATAERR, "redirect_port:" |
545 |
"invalid local port range"); |
546 |
if (GETNUMPORTS(portRange) != 1) |
547 |
errx(EX_DATAERR, "redirect_port: " |
548 |
"local port must be single in " |
549 |
"this context"); |
550 |
spool->port = GETLOPORT(portRange); |
551 |
} |
552 |
r->spool_cnt++; |
553 |
/* Point to the next possible cfg_spool. */ |
554 |
buf = &buf[sizeof(struct cfg_spool)]; |
555 |
sep = strtok(NULL, ","); |
556 |
} |
557 |
} |
558 |
|
559 |
return (space); |
560 |
} |
561 |
|
562 |
static int |
563 |
setup_redir_proto(char *buf, int *ac, char ***av) |
564 |
{ |
565 |
struct cfg_redir *r; |
566 |
struct protoent *protoent; |
567 |
size_t space; |
568 |
|
569 |
r = (struct cfg_redir *)buf; |
570 |
r->mode = REDIR_PROTO; |
571 |
/* Skip cfg_redir at beginning of buf. */ |
572 |
buf = &buf[sizeof(struct cfg_redir)]; |
573 |
space = sizeof(struct cfg_redir); |
574 |
|
575 |
/* |
576 |
* Extract protocol. |
577 |
*/ |
578 |
protoent = getprotobyname(**av); |
579 |
if (protoent == NULL) |
580 |
errx(EX_DATAERR, "redirect_proto: unknown protocol %s", **av); |
581 |
else |
582 |
r->proto = protoent->p_proto; |
583 |
|
584 |
(*av)++; (*ac)--; |
585 |
|
586 |
/* |
587 |
* Extract local address. |
588 |
*/ |
589 |
StrToAddr(**av, &r->laddr); |
590 |
|
591 |
(*av)++; (*ac)--; |
592 |
|
593 |
/* |
594 |
* Extract optional public address. |
595 |
*/ |
596 |
if (*ac == 0) { |
597 |
r->paddr.s_addr = INADDR_ANY; |
598 |
r->raddr.s_addr = INADDR_ANY; |
599 |
} else { |
600 |
/* see above in setup_redir_port() */ |
601 |
if (isdigit(***av)) { |
602 |
StrToAddr(**av, &r->paddr); |
603 |
(*av)++; (*ac)--; |
604 |
|
605 |
/* |
606 |
* Extract optional remote address. |
607 |
*/ |
608 |
/* see above in setup_redir_port() */ |
609 |
if (*ac != 0 && isdigit(***av)) { |
610 |
StrToAddr(**av, &r->raddr); |
611 |
(*av)++; (*ac)--; |
612 |
} |
613 |
} |
614 |
} |
615 |
|
616 |
return (space); |
617 |
} |
618 |
|
619 |
static void |
620 |
print_nat_config(unsigned char *buf) |
621 |
{ |
622 |
struct cfg_nat *n; |
623 |
int i, cnt, flag, off; |
624 |
struct cfg_redir *t; |
625 |
struct cfg_spool *s; |
626 |
struct protoent *p; |
627 |
|
628 |
n = (struct cfg_nat *)buf; |
629 |
flag = 1; |
630 |
off = sizeof(*n); |
631 |
printf("ipfw nat %u config", n->id); |
632 |
if (strlen(n->if_name) != 0) |
633 |
printf(" if %s", n->if_name); |
634 |
else if (n->ip.s_addr != 0) |
635 |
printf(" ip %s", inet_ntoa(n->ip)); |
636 |
while (n->mode != 0) { |
637 |
if (n->mode & PKT_ALIAS_LOG) { |
638 |
printf(" log"); |
639 |
n->mode &= ~PKT_ALIAS_LOG; |
640 |
} else if (n->mode & PKT_ALIAS_DENY_INCOMING) { |
641 |
printf(" deny_in"); |
642 |
n->mode &= ~PKT_ALIAS_DENY_INCOMING; |
643 |
} else if (n->mode & PKT_ALIAS_SAME_PORTS) { |
644 |
printf(" same_ports"); |
645 |
n->mode &= ~PKT_ALIAS_SAME_PORTS; |
646 |
} else if (n->mode & PKT_ALIAS_SKIP_GLOBAL) { |
647 |
printf(" skip_global"); |
648 |
n->mode &= ~PKT_ALIAS_SKIP_GLOBAL; |
649 |
} else if (n->mode & PKT_ALIAS_UNREGISTERED_ONLY) { |
650 |
printf(" unreg_only"); |
651 |
n->mode &= ~PKT_ALIAS_UNREGISTERED_ONLY; |
652 |
} else if (n->mode & PKT_ALIAS_RESET_ON_ADDR_CHANGE) { |
653 |
printf(" reset"); |
654 |
n->mode &= ~PKT_ALIAS_RESET_ON_ADDR_CHANGE; |
655 |
} else if (n->mode & PKT_ALIAS_REVERSE) { |
656 |
printf(" reverse"); |
657 |
n->mode &= ~PKT_ALIAS_REVERSE; |
658 |
} else if (n->mode & PKT_ALIAS_PROXY_ONLY) { |
659 |
printf(" proxy_only"); |
660 |
n->mode &= ~PKT_ALIAS_PROXY_ONLY; |
661 |
} |
662 |
} |
663 |
/* Print all the redirect's data configuration. */ |
664 |
for (cnt = 0; cnt < n->redir_cnt; cnt++) { |
665 |
t = (struct cfg_redir *)&buf[off]; |
666 |
off += SOF_REDIR; |
667 |
switch (t->mode) { |
668 |
case REDIR_ADDR: |
669 |
printf(" redirect_addr"); |
670 |
if (t->spool_cnt == 0) |
671 |
printf(" %s", inet_ntoa(t->laddr)); |
672 |
else |
673 |
for (i = 0; i < t->spool_cnt; i++) { |
674 |
s = (struct cfg_spool *)&buf[off]; |
675 |
if (i) |
676 |
printf(","); |
677 |
else |
678 |
printf(" "); |
679 |
printf("%s", inet_ntoa(s->addr)); |
680 |
off += SOF_SPOOL; |
681 |
} |
682 |
printf(" %s", inet_ntoa(t->paddr)); |
683 |
break; |
684 |
case REDIR_PORT: |
685 |
p = getprotobynumber(t->proto); |
686 |
printf(" redirect_port %s ", p->p_name); |
687 |
if (!t->spool_cnt) { |
688 |
printf("%s:%u", inet_ntoa(t->laddr), t->lport); |
689 |
if (t->pport_cnt > 1) |
690 |
printf("-%u", t->lport + |
691 |
t->pport_cnt - 1); |
692 |
} else |
693 |
for (i=0; i < t->spool_cnt; i++) { |
694 |
s = (struct cfg_spool *)&buf[off]; |
695 |
if (i) |
696 |
printf(","); |
697 |
printf("%s:%u", inet_ntoa(s->addr), |
698 |
s->port); |
699 |
off += SOF_SPOOL; |
700 |
} |
701 |
|
702 |
printf(" "); |
703 |
if (t->paddr.s_addr) |
704 |
printf("%s:", inet_ntoa(t->paddr)); |
705 |
printf("%u", t->pport); |
706 |
if (!t->spool_cnt && t->pport_cnt > 1) |
707 |
printf("-%u", t->pport + t->pport_cnt - 1); |
708 |
|
709 |
if (t->raddr.s_addr) { |
710 |
printf(" %s", inet_ntoa(t->raddr)); |
711 |
if (t->rport) { |
712 |
printf(":%u", t->rport); |
713 |
if (!t->spool_cnt && t->rport_cnt > 1) |
714 |
printf("-%u", t->rport + |
715 |
t->rport_cnt - 1); |
716 |
} |
717 |
} |
718 |
break; |
719 |
case REDIR_PROTO: |
720 |
p = getprotobynumber(t->proto); |
721 |
printf(" redirect_proto %s %s", p->p_name, |
722 |
inet_ntoa(t->laddr)); |
723 |
if (t->paddr.s_addr != 0) { |
724 |
printf(" %s", inet_ntoa(t->paddr)); |
725 |
if (t->raddr.s_addr) |
726 |
printf(" %s", inet_ntoa(t->raddr)); |
727 |
} |
728 |
break; |
729 |
default: |
730 |
errx(EX_DATAERR, "unknown redir mode"); |
731 |
break; |
732 |
} |
733 |
} |
734 |
printf("\n"); |
735 |
} |
736 |
|
737 |
void |
738 |
ipfw_config_nat(int ac, char **av) |
739 |
{ |
740 |
struct cfg_nat *n; /* Nat instance configuration. */ |
741 |
int i, off, tok, ac1; |
742 |
char *id, *buf, **av1, *end; |
743 |
size_t len; |
744 |
|
745 |
av++; |
746 |
ac--; |
747 |
/* Nat id. */ |
748 |
if (ac == 0) |
749 |
errx(EX_DATAERR, "missing nat id"); |
750 |
id = *av; |
751 |
i = (int)strtol(id, &end, 0); |
752 |
if (i <= 0 || *end != '\0') |
753 |
errx(EX_DATAERR, "illegal nat id: %s", id); |
754 |
av++; |
755 |
ac--; |
756 |
if (ac == 0) |
757 |
errx(EX_DATAERR, "missing option"); |
758 |
|
759 |
len = sizeof(struct cfg_nat); |
760 |
ac1 = ac; |
761 |
av1 = av; |
762 |
while (ac1 > 0) { |
763 |
tok = match_token(nat_params, *av1); |
764 |
ac1--; |
765 |
av1++; |
766 |
switch (tok) { |
767 |
case TOK_IP: |
768 |
case TOK_IF: |
769 |
ac1--; |
770 |
av1++; |
771 |
break; |
772 |
case TOK_ALOG: |
773 |
case TOK_DENY_INC: |
774 |
case TOK_SAME_PORTS: |
775 |
case TOK_SKIP_GLOBAL: |
776 |
case TOK_UNREG_ONLY: |
777 |
case TOK_RESET_ADDR: |
778 |
case TOK_ALIAS_REV: |
779 |
case TOK_PROXY_ONLY: |
780 |
break; |
781 |
case TOK_REDIR_ADDR: |
782 |
if (ac1 < 2) |
783 |
errx(EX_DATAERR, "redirect_addr: " |
784 |
"not enough arguments"); |
785 |
len += estimate_redir_addr(&ac1, &av1); |
786 |
av1 += 2; |
787 |
ac1 -= 2; |
788 |
break; |
789 |
case TOK_REDIR_PORT: |
790 |
if (ac1 < 3) |
791 |
errx(EX_DATAERR, "redirect_port: " |
792 |
"not enough arguments"); |
793 |
av1++; |
794 |
ac1--; |
795 |
len += estimate_redir_port(&ac1, &av1); |
796 |
av1 += 2; |
797 |
ac1 -= 2; |
798 |
/* Skip optional remoteIP/port */ |
799 |
if (ac1 != 0 && isdigit(**av1)) { |
800 |
av1++; |
801 |
ac1--; |
802 |
} |
803 |
break; |
804 |
case TOK_REDIR_PROTO: |
805 |
if (ac1 < 2) |
806 |
errx(EX_DATAERR, "redirect_proto: " |
807 |
"not enough arguments"); |
808 |
len += sizeof(struct cfg_redir); |
809 |
av1 += 2; |
810 |
ac1 -= 2; |
811 |
/* Skip optional remoteIP/port */ |
812 |
if (ac1 != 0 && isdigit(**av1)) { |
813 |
av1++; |
814 |
ac1--; |
815 |
} |
816 |
if (ac1 != 0 && isdigit(**av1)) { |
817 |
av1++; |
818 |
ac1--; |
819 |
} |
820 |
break; |
821 |
default: |
822 |
errx(EX_DATAERR, "unrecognised option ``%s''", av1[-1]); |
823 |
} |
824 |
} |
825 |
|
826 |
if ((buf = malloc(len)) == NULL) |
827 |
errx(EX_OSERR, "malloc failed"); |
828 |
|
829 |
/* Offset in buf: save space for n at the beginning. */ |
830 |
off = sizeof(*n); |
831 |
memset(buf, 0, len); |
832 |
n = (struct cfg_nat *)buf; |
833 |
n->id = i; |
834 |
|
835 |
while (ac > 0) { |
836 |
tok = match_token(nat_params, *av); |
837 |
ac--; |
838 |
av++; |
839 |
switch (tok) { |
840 |
case TOK_IP: |
841 |
if (ac == 0) |
842 |
errx(EX_DATAERR, "missing option"); |
843 |
if (!inet_aton(av[0], &(n->ip))) |
844 |
errx(EX_DATAERR, "bad ip address ``%s''", |
845 |
av[0]); |
846 |
ac--; |
847 |
av++; |
848 |
break; |
849 |
case TOK_IF: |
850 |
if (ac == 0) |
851 |
errx(EX_DATAERR, "missing option"); |
852 |
set_addr_dynamic(av[0], n); |
853 |
ac--; |
854 |
av++; |
855 |
break; |
856 |
case TOK_ALOG: |
857 |
n->mode |= PKT_ALIAS_LOG; |
858 |
break; |
859 |
case TOK_DENY_INC: |
860 |
n->mode |= PKT_ALIAS_DENY_INCOMING; |
861 |
break; |
862 |
case TOK_SAME_PORTS: |
863 |
n->mode |= PKT_ALIAS_SAME_PORTS; |
864 |
break; |
865 |
case TOK_UNREG_ONLY: |
866 |
n->mode |= PKT_ALIAS_UNREGISTERED_ONLY; |
867 |
break; |
868 |
case TOK_SKIP_GLOBAL: |
869 |
n->mode |= PKT_ALIAS_SKIP_GLOBAL; |
870 |
break; |
871 |
case TOK_RESET_ADDR: |
872 |
n->mode |= PKT_ALIAS_RESET_ON_ADDR_CHANGE; |
873 |
break; |
874 |
case TOK_ALIAS_REV: |
875 |
n->mode |= PKT_ALIAS_REVERSE; |
876 |
break; |
877 |
case TOK_PROXY_ONLY: |
878 |
n->mode |= PKT_ALIAS_PROXY_ONLY; |
879 |
break; |
880 |
/* |
881 |
* All the setup_redir_* functions work directly in |
882 |
* the final buffer, see above for details. |
883 |
*/ |
884 |
case TOK_REDIR_ADDR: |
885 |
case TOK_REDIR_PORT: |
886 |
case TOK_REDIR_PROTO: |
887 |
switch (tok) { |
888 |
case TOK_REDIR_ADDR: |
889 |
i = setup_redir_addr(&buf[off], &ac, &av); |
890 |
break; |
891 |
case TOK_REDIR_PORT: |
892 |
i = setup_redir_port(&buf[off], &ac, &av); |
893 |
break; |
894 |
case TOK_REDIR_PROTO: |
895 |
i = setup_redir_proto(&buf[off], &ac, &av); |
896 |
break; |
897 |
} |
898 |
n->redir_cnt++; |
899 |
off += i; |
900 |
break; |
901 |
} |
902 |
} |
903 |
|
904 |
i = do_cmd(IP_FW_NAT_CFG, buf, off); |
905 |
if (i) |
906 |
err(1, "setsockopt(%s)", "IP_FW_NAT_CFG"); |
907 |
|
908 |
if (!co.do_quiet) { |
909 |
/* After every modification, we show the resultant rule. */ |
910 |
int _ac = 3; |
911 |
const char *_av[] = {"show", "config", id}; |
912 |
ipfw_show_nat(_ac, (char **)(void *)_av); |
913 |
} |
914 |
} |
915 |
|
916 |
|
917 |
void |
918 |
ipfw_show_nat(int ac, char **av) |
919 |
{ |
920 |
struct cfg_nat *n; |
921 |
struct cfg_redir *e; |
922 |
int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size; |
923 |
int nat_cnt, redir_cnt, r; |
924 |
uint8_t *data, *p; |
925 |
char *endptr; |
926 |
|
927 |
do_rule = 0; |
928 |
nalloc = 1024; |
929 |
size = 0; |
930 |
data = NULL; |
931 |
frule = 0; |
932 |
lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */ |
933 |
ac--; |
934 |
av++; |
935 |
|
936 |
if (co.test_only) |
937 |
return; |
938 |
|
939 |
/* Parse parameters. */ |
940 |
for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) { |
941 |
if (!strncmp(av[0], "config", strlen(av[0]))) { |
942 |
cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1; |
943 |
continue; |
944 |
} |
945 |
/* Convert command line rule #. */ |
946 |
frule = lrule = strtoul(av[0], &endptr, 10); |
947 |
if (*endptr == '-') |
948 |
lrule = strtoul(endptr+1, &endptr, 10); |
949 |
if (lrule == 0) |
950 |
err(EX_USAGE, "invalid rule number: %s", av[0]); |
951 |
do_rule = 1; |
952 |
} |
953 |
|
954 |
nbytes = nalloc; |
955 |
while (nbytes >= nalloc) { |
956 |
nalloc = nalloc * 2; |
957 |
nbytes = nalloc; |
958 |
data = safe_realloc(data, nbytes); |
959 |
if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0) |
960 |
err(EX_OSERR, "getsockopt(IP_FW_GET_%s)", |
961 |
(cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG"); |
962 |
} |
963 |
if (nbytes == 0) |
964 |
exit(0); |
965 |
if (do_cfg) { |
966 |
nat_cnt = *((int *)data); |
967 |
for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) { |
968 |
n = (struct cfg_nat *)&data[i]; |
969 |
if (frule <= n->id && lrule >= n->id) |
970 |
print_nat_config(&data[i]); |
971 |
i += sizeof(struct cfg_nat); |
972 |
for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) { |
973 |
e = (struct cfg_redir *)&data[i]; |
974 |
i += sizeof(struct cfg_redir) + e->spool_cnt * |
975 |
sizeof(struct cfg_spool); |
976 |
} |
977 |
} |
978 |
} else { |
979 |
for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) { |
980 |
p = &data[i]; |
981 |
if (p == data + nbytes) |
982 |
break; |
983 |
bcopy(p, &r, sizeof(int)); |
984 |
if (do_rule) { |
985 |
if (!(frule <= r && lrule >= r)) |
986 |
continue; |
987 |
} |
988 |
printf("nat %u: %s\n", r, p+sizeof(int)); |
989 |
} |
990 |
} |
991 |
} |