]> git.saurik.com Git - apple/network_cmds.git/blob - natd.tproj/natd.c
network_cmds-115.tar.gz
[apple/network_cmds.git] / natd.tproj / natd.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * natd - Network Address Translation Daemon for FreeBSD.
24 *
25 * This software is provided free of charge, with no
26 * warranty of any kind, either expressed or implied.
27 * Use at your own risk.
28 *
29 * You may copy, modify and distribute this software (natd.c) freely.
30 *
31 * Ari Suutari <suutari@iki.fi>
32 *
33 * Based upon:
34 * $FreeBSD: src/sbin/natd/natd.c,v 1.25.2.3 2000/07/11 20:00:57 ru Exp $
35 */
36
37 #define SYSLOG_NAMES
38
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <sys/time.h>
43
44 #include <netinet/in.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/tcp.h>
48 #include <netinet/udp.h>
49 #include <netinet/ip_icmp.h>
50 #include <net/if.h>
51 #include <net/if_dl.h>
52 #include <net/route.h>
53 #include <arpa/inet.h>
54
55 #include <alias.h>
56 #include <ctype.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <netdb.h>
60 #include <signal.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <syslog.h>
65 #include <unistd.h>
66
67 #include "natd.h"
68
69 /*
70 * Default values for input and output
71 * divert socket ports.
72 */
73
74 #define DEFAULT_SERVICE "natd"
75
76 /*
77 * Definition of a port range, and macros to deal with values.
78 * FORMAT: HI 16-bits == first port in range, 0 == all ports.
79 * LO 16-bits == number of ports in range
80 * NOTES: - Port values are not stored in network byte order.
81 */
82
83 typedef u_long port_range;
84
85 #define GETLOPORT(x) ((x) >> 0x10)
86 #define GETNUMPORTS(x) ((x) & 0x0000ffff)
87 #define GETHIPORT(x) (GETLOPORT((x)) + GETNUMPORTS((x)))
88
89 /* Set y to be the low-port value in port_range variable x. */
90 #define SETLOPORT(x,y) ((x) = ((x) & 0x0000ffff) | ((y) << 0x10))
91
92 /* Set y to be the number of ports in port_range variable x. */
93 #define SETNUMPORTS(x,y) ((x) = ((x) & 0xffff0000) | (y))
94
95 /*
96 * Function prototypes.
97 */
98
99 static void DoAliasing (int fd, int direction);
100 static void DaemonMode (void);
101 static void HandleRoutingInfo (int fd);
102 static void Usage (void);
103 static char* FormatPacket (struct ip*);
104 static void PrintPacket (struct ip*);
105 static void SyslogPacket (struct ip*, int priority, const char *label);
106 static void SetAliasAddressFromIfName (const char *ifName);
107 static void InitiateShutdown (int);
108 static void Shutdown (int);
109 static void RefreshAddr (int);
110 static void ParseOption (const char* option, const char* parms);
111 static void ReadConfigFile (const char* fileName);
112 static void SetupPortRedirect (const char* parms);
113 static void SetupProtoRedirect(const char* parms);
114 static void SetupAddressRedirect (const char* parms);
115 static void StrToAddr (const char* str, struct in_addr* addr);
116 static u_short StrToPort (const char* str, const char* proto);
117 static int StrToPortRange (const char* str, const char* proto, port_range *portRange);
118 static int StrToProto (const char* str);
119 static int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange);
120 static void ParseArgs (int argc, char** argv);
121 static void FlushPacketBuffer (int fd);
122 static void SetupPunchFW(const char *strValue);
123
124 /*
125 * Globals.
126 */
127
128 static int verbose;
129 static int background;
130 static int running;
131 static int assignAliasAddr;
132 static char* ifName;
133 static int ifIndex;
134 static u_short inPort;
135 static u_short outPort;
136 static u_short inOutPort;
137 static struct in_addr aliasAddr;
138 static int dynamicMode;
139 static int ifMTU;
140 static int aliasOverhead;
141 static int icmpSock;
142 static char packetBuf[IP_MAXPACKET];
143 static int packetLen;
144 static struct sockaddr_in packetAddr;
145 static int packetSock;
146 static int packetDirection;
147 static int dropIgnoredIncoming;
148 static int logDropped;
149 static int logFacility;
150
151 int main (int argc, char** argv)
152 {
153 int divertIn;
154 int divertOut;
155 int divertInOut;
156 int routeSock;
157 struct sockaddr_in addr;
158 fd_set readMask;
159 fd_set writeMask;
160 int fdMax;
161 /*
162 * Initialize packet aliasing software.
163 * Done already here to be able to alter option bits
164 * during command line and configuration file processing.
165 */
166 PacketAliasInit ();
167 /*
168 * Parse options.
169 */
170 inPort = 0;
171 outPort = 0;
172 verbose = 0;
173 inOutPort = 0;
174 ifName = NULL;
175 ifMTU = -1;
176 background = 0;
177 running = 1;
178 assignAliasAddr = 0;
179 aliasAddr.s_addr = INADDR_NONE;
180 aliasOverhead = 12;
181 dynamicMode = 0;
182 logDropped = 0;
183 logFacility = LOG_DAEMON;
184 /*
185 * Mark packet buffer empty.
186 */
187 packetSock = -1;
188 packetDirection = DONT_KNOW;
189
190 ParseArgs (argc, argv);
191 /*
192 * Open syslog channel.
193 */
194 openlog ("natd", LOG_CONS | LOG_PID | (verbose ? LOG_PERROR : 0),
195 logFacility);
196 /*
197 * Check that valid aliasing address has been given.
198 */
199 if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
200 errx (1, "aliasing address not given");
201
202 if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
203 errx (1, "both alias address and interface "
204 "name are not allowed");
205 /*
206 * Check that valid port number is known.
207 */
208 if (inPort != 0 || outPort != 0)
209 if (inPort == 0 || outPort == 0)
210 errx (1, "both input and output ports are required");
211
212 if (inPort == 0 && outPort == 0 && inOutPort == 0)
213 ParseOption ("port", DEFAULT_SERVICE);
214
215 /*
216 * Check if ignored packets should be dropped.
217 */
218 dropIgnoredIncoming = PacketAliasSetMode (0, 0);
219 dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
220 /*
221 * Create divert sockets. Use only one socket if -p was specified
222 * on command line. Otherwise, create separate sockets for
223 * outgoing and incoming connnections.
224 */
225 if (inOutPort) {
226
227 divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
228 if (divertInOut == -1)
229 Quit ("Unable to create divert socket.");
230
231 divertIn = -1;
232 divertOut = -1;
233 /*
234 * Bind socket.
235 */
236
237 addr.sin_family = AF_INET;
238 addr.sin_addr.s_addr = INADDR_ANY;
239 addr.sin_port = inOutPort;
240
241 if (bind (divertInOut,
242 (struct sockaddr*) &addr,
243 sizeof addr) == -1)
244 Quit ("Unable to bind divert socket.");
245 }
246 else {
247
248 divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
249 if (divertIn == -1)
250 Quit ("Unable to create incoming divert socket.");
251
252 divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
253 if (divertOut == -1)
254 Quit ("Unable to create outgoing divert socket.");
255
256 divertInOut = -1;
257
258 /*
259 * Bind divert sockets.
260 */
261
262 addr.sin_family = AF_INET;
263 addr.sin_addr.s_addr = INADDR_ANY;
264 addr.sin_port = inPort;
265
266 if (bind (divertIn,
267 (struct sockaddr*) &addr,
268 sizeof addr) == -1)
269 Quit ("Unable to bind incoming divert socket.");
270
271 addr.sin_family = AF_INET;
272 addr.sin_addr.s_addr = INADDR_ANY;
273 addr.sin_port = outPort;
274
275 if (bind (divertOut,
276 (struct sockaddr*) &addr,
277 sizeof addr) == -1)
278 Quit ("Unable to bind outgoing divert socket.");
279 }
280 /*
281 * Create routing socket if interface name specified and in dynamic mode.
282 */
283 routeSock = -1;
284 if (ifName) {
285 if (dynamicMode) {
286
287 routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
288 if (routeSock == -1)
289 Quit ("Unable to create routing info socket.");
290
291 assignAliasAddr = 1;
292 }
293 else
294 SetAliasAddressFromIfName (ifName);
295 }
296 /*
297 * Create socket for sending ICMP messages.
298 */
299 icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
300 if (icmpSock == -1)
301 Quit ("Unable to create ICMP socket.");
302
303 /*
304 * And disable reads for the socket, otherwise it slowly fills
305 * up with received icmps which we do not use.
306 */
307 shutdown(icmpSock, SHUT_RD);
308
309 /*
310 * Become a daemon unless verbose mode was requested.
311 */
312 if (!verbose)
313 DaemonMode ();
314 /*
315 * Catch signals to manage shutdown and
316 * refresh of interface address.
317 */
318 siginterrupt(SIGTERM, 1);
319 siginterrupt(SIGHUP, 1);
320 signal (SIGTERM, InitiateShutdown);
321 signal (SIGHUP, RefreshAddr);
322 /*
323 * Set alias address if it has been given.
324 */
325 if (aliasAddr.s_addr != INADDR_NONE)
326 PacketAliasSetAddress (aliasAddr);
327 /*
328 * We need largest descriptor number for select.
329 */
330
331 fdMax = -1;
332
333 if (divertIn > fdMax)
334 fdMax = divertIn;
335
336 if (divertOut > fdMax)
337 fdMax = divertOut;
338
339 if (divertInOut > fdMax)
340 fdMax = divertInOut;
341
342 if (routeSock > fdMax)
343 fdMax = routeSock;
344
345 while (running) {
346
347 if (divertInOut != -1 && !ifName && packetSock == -1) {
348 /*
349 * When using only one socket, just call
350 * DoAliasing repeatedly to process packets.
351 */
352 DoAliasing (divertInOut, DONT_KNOW);
353 continue;
354 }
355 /*
356 * Build read mask from socket descriptors to select.
357 */
358 FD_ZERO (&readMask);
359 FD_ZERO (&writeMask);
360
361 /*
362 * If there is unsent packet in buffer, use select
363 * to check when socket comes writable again.
364 */
365 if (packetSock != -1) {
366
367 FD_SET (packetSock, &writeMask);
368 }
369 else {
370 /*
371 * No unsent packet exists - safe to check if
372 * new ones are available.
373 */
374 if (divertIn != -1)
375 FD_SET (divertIn, &readMask);
376
377 if (divertOut != -1)
378 FD_SET (divertOut, &readMask);
379
380 if (divertInOut != -1)
381 FD_SET (divertInOut, &readMask);
382 }
383 /*
384 * Routing info is processed always.
385 */
386 if (routeSock != -1)
387 FD_SET (routeSock, &readMask);
388
389 if (select (fdMax + 1,
390 &readMask,
391 &writeMask,
392 NULL,
393 NULL) == -1) {
394
395 if (errno == EINTR)
396 continue;
397
398 Quit ("Select failed.");
399 }
400
401 if (packetSock != -1)
402 if (FD_ISSET (packetSock, &writeMask))
403 FlushPacketBuffer (packetSock);
404
405 if (divertIn != -1)
406 if (FD_ISSET (divertIn, &readMask))
407 DoAliasing (divertIn, INPUT);
408
409 if (divertOut != -1)
410 if (FD_ISSET (divertOut, &readMask))
411 DoAliasing (divertOut, OUTPUT);
412
413 if (divertInOut != -1)
414 if (FD_ISSET (divertInOut, &readMask))
415 DoAliasing (divertInOut, DONT_KNOW);
416
417 if (routeSock != -1)
418 if (FD_ISSET (routeSock, &readMask))
419 HandleRoutingInfo (routeSock);
420 }
421
422 if (background)
423 unlink (PIDFILE);
424
425 return 0;
426 }
427
428 static void DaemonMode ()
429 {
430 FILE* pidFile;
431
432 daemon (0, 0);
433 background = 1;
434
435 pidFile = fopen (PIDFILE, "w");
436 if (pidFile) {
437
438 fprintf (pidFile, "%d\n", getpid ());
439 fclose (pidFile);
440 }
441 }
442
443 static void ParseArgs (int argc, char** argv)
444 {
445 int arg;
446 char* opt;
447 char parmBuf[256];
448 int len; /* bounds checking */
449
450 for (arg = 1; arg < argc; arg++) {
451
452 opt = argv[arg];
453 if (*opt != '-') {
454
455 warnx ("invalid option %s", opt);
456 Usage ();
457 }
458
459 parmBuf[0] = '\0';
460 len = 0;
461
462 while (arg < argc - 1) {
463
464 if (argv[arg + 1][0] == '-')
465 break;
466
467 if (len) {
468 strncat (parmBuf, " ", sizeof(parmBuf) - (len + 1));
469 len += strlen(parmBuf + len);
470 }
471
472 ++arg;
473 strncat (parmBuf, argv[arg], sizeof(parmBuf) - (len + 1));
474 len += strlen(parmBuf + len);
475
476 }
477
478 ParseOption (opt + 1, (len ? parmBuf : NULL));
479
480 }
481 }
482
483 static void DoAliasing (int fd, int direction)
484 {
485 int bytes;
486 int origBytes;
487 int status;
488 int addrSize;
489 struct ip* ip;
490
491 if (assignAliasAddr) {
492
493 SetAliasAddressFromIfName (ifName);
494 assignAliasAddr = 0;
495 }
496 /*
497 * Get packet from socket.
498 */
499 addrSize = sizeof packetAddr;
500 origBytes = recvfrom (fd,
501 packetBuf,
502 sizeof packetBuf,
503 0,
504 (struct sockaddr*) &packetAddr,
505 &addrSize);
506
507 if (origBytes == -1) {
508
509 if (errno != EINTR)
510 Warn ("read from divert socket failed");
511
512 return;
513 }
514 /*
515 * This is a IP packet.
516 */
517 ip = (struct ip*) packetBuf;
518 if (direction == DONT_KNOW) {
519 if (packetAddr.sin_addr.s_addr == INADDR_ANY)
520 direction = OUTPUT;
521 else
522 direction = INPUT;
523 }
524
525 if (verbose) {
526 /*
527 * Print packet direction and protocol type.
528 */
529 printf (direction == OUTPUT ? "Out " : "In ");
530
531 switch (ip->ip_p) {
532 case IPPROTO_TCP:
533 printf ("[TCP] ");
534 break;
535
536 case IPPROTO_UDP:
537 printf ("[UDP] ");
538 break;
539
540 case IPPROTO_ICMP:
541 printf ("[ICMP] ");
542 break;
543
544 default:
545 printf ("[%d] ", ip->ip_p);
546 break;
547 }
548 /*
549 * Print addresses.
550 */
551 PrintPacket (ip);
552 }
553
554 if (direction == OUTPUT) {
555 /*
556 * Outgoing packets. Do aliasing.
557 */
558 PacketAliasOut (packetBuf, IP_MAXPACKET);
559 }
560 else {
561
562 /*
563 * Do aliasing.
564 */
565 status = PacketAliasIn (packetBuf, IP_MAXPACKET);
566 if (status == PKT_ALIAS_IGNORED &&
567 dropIgnoredIncoming) {
568
569 if (verbose)
570 printf (" dropped.\n");
571
572 if (logDropped)
573 SyslogPacket (ip, LOG_WARNING, "denied");
574
575 return;
576 }
577 }
578 /*
579 * Length might have changed during aliasing.
580 */
581 bytes = ntohs (ip->ip_len);
582 /*
583 * Update alias overhead size for outgoing packets.
584 */
585 if (direction == OUTPUT &&
586 bytes - origBytes > aliasOverhead)
587 aliasOverhead = bytes - origBytes;
588
589 if (verbose) {
590
591 /*
592 * Print addresses after aliasing.
593 */
594 printf (" aliased to\n");
595 printf (" ");
596 PrintPacket (ip);
597 printf ("\n");
598 }
599
600 packetLen = bytes;
601 packetSock = fd;
602 packetDirection = direction;
603
604 FlushPacketBuffer (fd);
605 }
606
607 static void FlushPacketBuffer (int fd)
608 {
609 int wrote;
610 char msgBuf[80];
611 /*
612 * Put packet back for processing.
613 */
614 wrote = sendto (fd,
615 packetBuf,
616 packetLen,
617 0,
618 (struct sockaddr*) &packetAddr,
619 sizeof packetAddr);
620
621 if (wrote != packetLen) {
622 /*
623 * If buffer space is not available,
624 * just return. Main loop will take care of
625 * retrying send when space becomes available.
626 */
627 if (errno == ENOBUFS)
628 return;
629
630 if (errno == EMSGSIZE) {
631
632 if (packetDirection == OUTPUT &&
633 ifMTU != -1)
634 SendNeedFragIcmp (icmpSock,
635 (struct ip*) packetBuf,
636 ifMTU - aliasOverhead);
637 }
638 else {
639
640 sprintf (msgBuf, "failed to write packet back");
641 Warn (msgBuf);
642 }
643 }
644
645 packetSock = -1;
646 }
647
648 static void HandleRoutingInfo (int fd)
649 {
650 int bytes;
651 struct if_msghdr ifMsg;
652 /*
653 * Get packet from socket.
654 */
655 bytes = read (fd, &ifMsg, sizeof ifMsg);
656 if (bytes == -1) {
657
658 Warn ("read from routing socket failed");
659 return;
660 }
661
662 if (ifMsg.ifm_version != RTM_VERSION) {
663
664 Warn ("unexpected packet read from routing socket");
665 return;
666 }
667
668 if (verbose)
669 printf ("Routing message %#x received.\n", ifMsg.ifm_type);
670
671 if ((ifMsg.ifm_type == RTM_NEWADDR || ifMsg.ifm_type == RTM_IFINFO) &&
672 ifMsg.ifm_index == ifIndex) {
673 if (verbose)
674 printf("Interface address/MTU has probably changed.\n");
675 assignAliasAddr = 1;
676 }
677 }
678
679 static void PrintPacket (struct ip* ip)
680 {
681 printf ("%s", FormatPacket (ip));
682 }
683
684 static void SyslogPacket (struct ip* ip, int priority, const char *label)
685 {
686 syslog (priority, "%s %s", label, FormatPacket (ip));
687 }
688
689 static char* FormatPacket (struct ip* ip)
690 {
691 static char buf[256];
692 struct tcphdr* tcphdr;
693 struct udphdr* udphdr;
694 struct icmp* icmphdr;
695 char src[20];
696 char dst[20];
697
698 strcpy (src, inet_ntoa (ip->ip_src));
699 strcpy (dst, inet_ntoa (ip->ip_dst));
700
701 switch (ip->ip_p) {
702 case IPPROTO_TCP:
703 tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
704 sprintf (buf, "[TCP] %s:%d -> %s:%d",
705 src,
706 ntohs (tcphdr->th_sport),
707 dst,
708 ntohs (tcphdr->th_dport));
709 break;
710
711 case IPPROTO_UDP:
712 udphdr = (struct udphdr*) ((char*) ip + (ip->ip_hl << 2));
713 sprintf (buf, "[UDP] %s:%d -> %s:%d",
714 src,
715 ntohs (udphdr->uh_sport),
716 dst,
717 ntohs (udphdr->uh_dport));
718 break;
719
720 case IPPROTO_ICMP:
721 icmphdr = (struct icmp*) ((char*) ip + (ip->ip_hl << 2));
722 sprintf (buf, "[ICMP] %s -> %s %u(%u)",
723 src,
724 dst,
725 icmphdr->icmp_type,
726 icmphdr->icmp_code);
727 break;
728
729 default:
730 sprintf (buf, "[%d] %s -> %s ", ip->ip_p, src, dst);
731 break;
732 }
733
734 return buf;
735 }
736
737 static void
738 SetAliasAddressFromIfName(const char *ifn)
739 {
740 size_t needed;
741 int mib[6];
742 char *buf, *lim, *next;
743 struct if_msghdr *ifm;
744 struct ifa_msghdr *ifam;
745 struct sockaddr_dl *sdl;
746 struct sockaddr_in *sin;
747
748 mib[0] = CTL_NET;
749 mib[1] = PF_ROUTE;
750 mib[2] = 0;
751 mib[3] = AF_INET; /* Only IP addresses please */
752 mib[4] = NET_RT_IFLIST;
753 mib[5] = 0; /* ifIndex??? */
754 /*
755 * Get interface data.
756 */
757 if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
758 err(1, "iflist-sysctl-estimate");
759 if ((buf = malloc(needed)) == NULL)
760 errx(1, "malloc failed");
761 if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
762 err(1, "iflist-sysctl-get");
763 lim = buf + needed;
764 /*
765 * Loop through interfaces until one with
766 * given name is found. This is done to
767 * find correct interface index for routing
768 * message processing.
769 */
770 ifIndex = 0;
771 next = buf;
772 while (next < lim) {
773 ifm = (struct if_msghdr *)next;
774 next += ifm->ifm_msglen;
775 if (ifm->ifm_version != RTM_VERSION) {
776 if (verbose)
777 warnx("routing message version %d "
778 "not understood", ifm->ifm_version);
779 continue;
780 }
781 if (ifm->ifm_type == RTM_IFINFO) {
782 sdl = (struct sockaddr_dl *)(ifm + 1);
783 if (strlen(ifn) == sdl->sdl_nlen &&
784 strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) {
785 ifIndex = ifm->ifm_index;
786 ifMTU = ifm->ifm_data.ifi_mtu;
787 break;
788 }
789 }
790 }
791 if (!ifIndex)
792 errx(1, "unknown interface name %s", ifn);
793 /*
794 * Get interface address.
795 */
796 sin = NULL;
797 while (next < lim) {
798 ifam = (struct ifa_msghdr *)next;
799 next += ifam->ifam_msglen;
800 if (ifam->ifam_version != RTM_VERSION) {
801 if (verbose)
802 warnx("routing message version %d "
803 "not understood", ifam->ifam_version);
804 continue;
805 }
806 if (ifam->ifam_type != RTM_NEWADDR)
807 break;
808 if (ifam->ifam_addrs & RTA_IFA) {
809 int i;
810 char *cp = (char *)(ifam + 1);
811
812 #define ROUNDUP(a) \
813 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
814 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
815
816 for (i = 1; i < RTA_IFA; i <<= 1)
817 if (ifam->ifam_addrs & i)
818 ADVANCE(cp, (struct sockaddr *)cp);
819 if (((struct sockaddr *)cp)->sa_family == AF_INET) {
820 sin = (struct sockaddr_in *)cp;
821 break;
822 }
823 }
824 }
825 if (sin == NULL)
826 errx(1, "%s: cannot get interface address", ifn);
827
828 PacketAliasSetAddress(sin->sin_addr);
829 syslog(LOG_INFO, "Aliasing to %s, mtu %d bytes",
830 inet_ntoa(sin->sin_addr), ifMTU);
831
832 free(buf);
833 }
834
835 void Quit (const char* msg)
836 {
837 Warn (msg);
838 exit (1);
839 }
840
841 void Warn (const char* msg)
842 {
843 if (background)
844 syslog (LOG_ALERT, "%s (%m)", msg);
845 else
846 warn ("%s", msg);
847 }
848
849 static void RefreshAddr (int sig)
850 {
851 if (ifName)
852 assignAliasAddr = 1;
853 }
854
855 static void InitiateShutdown (int sig)
856 {
857 /*
858 * Start timer to allow kernel gracefully
859 * shutdown existing connections when system
860 * is shut down.
861 */
862 siginterrupt(SIGALRM, 1);
863 signal (SIGALRM, Shutdown);
864 alarm (10);
865 }
866
867 static void Shutdown (int sig)
868 {
869 running = 0;
870 }
871
872 /*
873 * Different options recognized by this program.
874 */
875
876 enum Option {
877
878 PacketAliasOption,
879 Verbose,
880 InPort,
881 OutPort,
882 Port,
883 AliasAddress,
884 TargetAddress,
885 InterfaceName,
886 RedirectPort,
887 RedirectProto,
888 RedirectAddress,
889 ConfigFile,
890 DynamicMode,
891 ProxyRule,
892 LogDenied,
893 LogFacility,
894 PunchFW
895 };
896
897 enum Param {
898
899 YesNo,
900 Numeric,
901 String,
902 None,
903 Address,
904 Service
905 };
906
907 /*
908 * Option information structure (used by ParseOption).
909 */
910
911 struct OptionInfo {
912
913 enum Option type;
914 int packetAliasOpt;
915 enum Param parm;
916 const char* parmDescription;
917 const char* description;
918 const char* name;
919 const char* shortName;
920 };
921
922 /*
923 * Table of known options.
924 */
925
926 static struct OptionInfo optionTable[] = {
927
928 { PacketAliasOption,
929 PKT_ALIAS_UNREGISTERED_ONLY,
930 YesNo,
931 "[yes|no]",
932 "alias only unregistered addresses",
933 "unregistered_only",
934 "u" },
935
936 { PacketAliasOption,
937 PKT_ALIAS_LOG,
938 YesNo,
939 "[yes|no]",
940 "enable logging",
941 "log",
942 "l" },
943
944 { PacketAliasOption,
945 PKT_ALIAS_PROXY_ONLY,
946 YesNo,
947 "[yes|no]",
948 "proxy only",
949 "proxy_only",
950 NULL },
951
952 { PacketAliasOption,
953 PKT_ALIAS_REVERSE,
954 YesNo,
955 "[yes|no]",
956 "operate in reverse mode",
957 "reverse",
958 NULL },
959
960 { PacketAliasOption,
961 PKT_ALIAS_DENY_INCOMING,
962 YesNo,
963 "[yes|no]",
964 "allow incoming connections",
965 "deny_incoming",
966 "d" },
967
968 { PacketAliasOption,
969 PKT_ALIAS_USE_SOCKETS,
970 YesNo,
971 "[yes|no]",
972 "use sockets to inhibit port conflict",
973 "use_sockets",
974 "s" },
975
976 { PacketAliasOption,
977 PKT_ALIAS_SAME_PORTS,
978 YesNo,
979 "[yes|no]",
980 "try to keep original port numbers for connections",
981 "same_ports",
982 "m" },
983
984 { Verbose,
985 0,
986 YesNo,
987 "[yes|no]",
988 "verbose mode, dump packet information",
989 "verbose",
990 "v" },
991
992 { DynamicMode,
993 0,
994 YesNo,
995 "[yes|no]",
996 "dynamic mode, automatically detect interface address changes",
997 "dynamic",
998 NULL },
999
1000 { InPort,
1001 0,
1002 Service,
1003 "number|service_name",
1004 "set port for incoming packets",
1005 "in_port",
1006 "i" },
1007
1008 { OutPort,
1009 0,
1010 Service,
1011 "number|service_name",
1012 "set port for outgoing packets",
1013 "out_port",
1014 "o" },
1015
1016 { Port,
1017 0,
1018 Service,
1019 "number|service_name",
1020 "set port (defaults to natd/divert)",
1021 "port",
1022 "p" },
1023
1024 { AliasAddress,
1025 0,
1026 Address,
1027 "x.x.x.x",
1028 "address to use for aliasing",
1029 "alias_address",
1030 "a" },
1031
1032 { TargetAddress,
1033 0,
1034 Address,
1035 "x.x.x.x",
1036 "address to use for incoming sessions",
1037 "target_address",
1038 "t" },
1039
1040 { InterfaceName,
1041 0,
1042 String,
1043 "network_if_name",
1044 "take aliasing address from interface",
1045 "interface",
1046 "n" },
1047
1048 { ProxyRule,
1049 0,
1050 String,
1051 "[type encode_ip_hdr|encode_tcp_stream] port xxxx server "
1052 "a.b.c.d:yyyy",
1053 "add transparent proxying / destination NAT",
1054 "proxy_rule",
1055 NULL },
1056
1057 { RedirectPort,
1058 0,
1059 String,
1060 "tcp|udp local_addr:local_port_range[,...] [public_addr:]public_port_range"
1061 " [remote_addr[:remote_port_range]]",
1062 "redirect a port (or ports) for incoming traffic",
1063 "redirect_port",
1064 NULL },
1065
1066 { RedirectProto,
1067 0,
1068 String,
1069 "proto local_addr [public_addr] [remote_addr]",
1070 "redirect packets of a given proto",
1071 "redirect_proto",
1072 NULL },
1073
1074 { RedirectAddress,
1075 0,
1076 String,
1077 "local_addr[,...] public_addr",
1078 "define mapping between local and public addresses",
1079 "redirect_address",
1080 NULL },
1081
1082 { ConfigFile,
1083 0,
1084 String,
1085 "file_name",
1086 "read options from configuration file",
1087 "config",
1088 "f" },
1089
1090 { LogDenied,
1091 0,
1092 YesNo,
1093 "[yes|no]",
1094 "enable logging of denied incoming packets",
1095 "log_denied",
1096 NULL },
1097
1098 { LogFacility,
1099 0,
1100 String,
1101 "facility",
1102 "name of syslog facility to use for logging",
1103 "log_facility",
1104 NULL },
1105
1106 { PunchFW,
1107 0,
1108 String,
1109 "basenumber:count",
1110 "punch holes in the firewall for incoming FTP/IRC DCC connections",
1111 "punch_fw",
1112 NULL }
1113 };
1114
1115 static void ParseOption (const char* option, const char* parms)
1116 {
1117 int i;
1118 struct OptionInfo* info;
1119 int yesNoValue;
1120 int aliasValue;
1121 int numValue;
1122 u_short uNumValue;
1123 const char* strValue;
1124 struct in_addr addrValue;
1125 int max;
1126 char* end;
1127 CODE* fac_record = NULL;
1128 /*
1129 * Find option from table.
1130 */
1131 max = sizeof (optionTable) / sizeof (struct OptionInfo);
1132 for (i = 0, info = optionTable; i < max; i++, info++) {
1133
1134 if (!strcmp (info->name, option))
1135 break;
1136
1137 if (info->shortName)
1138 if (!strcmp (info->shortName, option))
1139 break;
1140 }
1141
1142 if (i >= max) {
1143
1144 warnx ("unknown option %s", option);
1145 Usage ();
1146 }
1147
1148 uNumValue = 0;
1149 yesNoValue = 0;
1150 numValue = 0;
1151 strValue = NULL;
1152 /*
1153 * Check parameters.
1154 */
1155 switch (info->parm) {
1156 case YesNo:
1157 if (!parms)
1158 parms = "yes";
1159
1160 if (!strcmp (parms, "yes"))
1161 yesNoValue = 1;
1162 else
1163 if (!strcmp (parms, "no"))
1164 yesNoValue = 0;
1165 else
1166 errx (1, "%s needs yes/no parameter", option);
1167 break;
1168
1169 case Service:
1170 if (!parms)
1171 errx (1, "%s needs service name or "
1172 "port number parameter",
1173 option);
1174
1175 uNumValue = StrToPort (parms, "divert");
1176 break;
1177
1178 case Numeric:
1179 if (parms)
1180 numValue = strtol (parms, &end, 10);
1181 else
1182 end = NULL;
1183
1184 if (end == parms)
1185 errx (1, "%s needs numeric parameter", option);
1186 break;
1187
1188 case String:
1189 strValue = parms;
1190 if (!strValue)
1191 errx (1, "%s needs parameter", option);
1192 break;
1193
1194 case None:
1195 if (parms)
1196 errx (1, "%s does not take parameters", option);
1197 break;
1198
1199 case Address:
1200 if (!parms)
1201 errx (1, "%s needs address/host parameter", option);
1202
1203 StrToAddr (parms, &addrValue);
1204 break;
1205 }
1206
1207 switch (info->type) {
1208 case PacketAliasOption:
1209
1210 aliasValue = yesNoValue ? info->packetAliasOpt : 0;
1211 PacketAliasSetMode (aliasValue, info->packetAliasOpt);
1212 break;
1213
1214 case Verbose:
1215 verbose = yesNoValue;
1216 break;
1217
1218 case DynamicMode:
1219 dynamicMode = yesNoValue;
1220 break;
1221
1222 case InPort:
1223 inPort = uNumValue;
1224 break;
1225
1226 case OutPort:
1227 outPort = uNumValue;
1228 break;
1229
1230 case Port:
1231 inOutPort = uNumValue;
1232 break;
1233
1234 case AliasAddress:
1235 memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
1236 break;
1237
1238 case TargetAddress:
1239 PacketAliasSetTarget(addrValue);
1240 break;
1241
1242 case RedirectPort:
1243 SetupPortRedirect (strValue);
1244 break;
1245
1246 case RedirectProto:
1247 SetupProtoRedirect(strValue);
1248 break;
1249
1250 case RedirectAddress:
1251 SetupAddressRedirect (strValue);
1252 break;
1253
1254 case ProxyRule:
1255 PacketAliasProxyRule (strValue);
1256 break;
1257
1258 case InterfaceName:
1259 if (ifName)
1260 free (ifName);
1261
1262 ifName = strdup (strValue);
1263 break;
1264
1265 case ConfigFile:
1266 ReadConfigFile (strValue);
1267 break;
1268
1269 case LogDenied:
1270 logDropped = 1;
1271 break;
1272
1273 case LogFacility:
1274
1275 fac_record = facilitynames;
1276 while (fac_record->c_name != NULL) {
1277
1278 if (!strcmp (fac_record->c_name, strValue)) {
1279
1280 logFacility = fac_record->c_val;
1281 break;
1282
1283 }
1284 else
1285 fac_record++;
1286 }
1287
1288 if(fac_record->c_name == NULL)
1289 errx(1, "Unknown log facility name: %s", strValue);
1290
1291 break;
1292
1293 case PunchFW:
1294 SetupPunchFW(strValue);
1295 break;
1296 }
1297 }
1298
1299 void ReadConfigFile (const char* fileName)
1300 {
1301 FILE* file;
1302 char *buf;
1303 size_t len;
1304 char *ptr, *p;
1305 char* option;
1306
1307 file = fopen (fileName, "r");
1308 if (!file)
1309 err(1, "cannot open config file %s", fileName);
1310
1311 while ((buf = fgetln(file, &len)) != NULL) {
1312 if (buf[len - 1] == '\n')
1313 buf[len - 1] = '\0';
1314 else
1315 errx(1, "config file format error: "
1316 "last line should end with newline");
1317
1318 /*
1319 * Check for comments, strip off trailing spaces.
1320 */
1321 if ((ptr = strchr(buf, '#')))
1322 *ptr = '\0';
1323 for (ptr = buf; isspace(*ptr); ++ptr)
1324 continue;
1325 if (*ptr == '\0')
1326 continue;
1327 for (p = strchr(buf, '\0'); isspace(*--p);)
1328 continue;
1329 *++p = '\0';
1330
1331 /*
1332 * Extract option name.
1333 */
1334 option = ptr;
1335 while (*ptr && !isspace (*ptr))
1336 ++ptr;
1337
1338 if (*ptr != '\0') {
1339
1340 *ptr = '\0';
1341 ++ptr;
1342 }
1343 /*
1344 * Skip white space between name and parms.
1345 */
1346 while (*ptr && isspace (*ptr))
1347 ++ptr;
1348
1349 ParseOption (option, *ptr ? ptr : NULL);
1350 }
1351
1352 fclose (file);
1353 }
1354
1355 static void Usage ()
1356 {
1357 int i;
1358 int max;
1359 struct OptionInfo* info;
1360
1361 fprintf (stderr, "Recognized options:\n\n");
1362
1363 max = sizeof (optionTable) / sizeof (struct OptionInfo);
1364 for (i = 0, info = optionTable; i < max; i++, info++) {
1365
1366 fprintf (stderr, "-%-20s %s\n", info->name,
1367 info->parmDescription);
1368
1369 if (info->shortName)
1370 fprintf (stderr, "-%-20s %s\n", info->shortName,
1371 info->parmDescription);
1372
1373 fprintf (stderr, " %s\n\n", info->description);
1374 }
1375
1376 exit (1);
1377 }
1378
1379 void SetupPortRedirect (const char* parms)
1380 {
1381 char buf[128];
1382 char* ptr;
1383 char* serverPool;
1384 struct in_addr localAddr;
1385 struct in_addr publicAddr;
1386 struct in_addr remoteAddr;
1387 port_range portRange;
1388 u_short localPort = 0;
1389 u_short publicPort = 0;
1390 u_short remotePort = 0;
1391 u_short numLocalPorts = 0;
1392 u_short numPublicPorts = 0;
1393 u_short numRemotePorts = 0;
1394 int proto;
1395 char* protoName;
1396 char* separator;
1397 int i;
1398 struct alias_link *link = NULL;
1399
1400 strcpy (buf, parms);
1401 /*
1402 * Extract protocol.
1403 */
1404 protoName = strtok (buf, " \t");
1405 if (!protoName)
1406 errx (1, "redirect_port: missing protocol");
1407
1408 proto = StrToProto (protoName);
1409 /*
1410 * Extract local address.
1411 */
1412 ptr = strtok (NULL, " \t");
1413 if (!ptr)
1414 errx (1, "redirect_port: missing local address");
1415
1416 separator = strchr(ptr, ',');
1417 if (separator) { /* LSNAT redirection syntax. */
1418 localAddr.s_addr = INADDR_NONE;
1419 localPort = ~0;
1420 numLocalPorts = 1;
1421 serverPool = ptr;
1422 } else {
1423 if ( StrToAddrAndPortRange (ptr, &localAddr, protoName, &portRange) != 0 )
1424 errx (1, "redirect_port: invalid local port range");
1425
1426 localPort = GETLOPORT(portRange);
1427 numLocalPorts = GETNUMPORTS(portRange);
1428 serverPool = NULL;
1429 }
1430
1431 /*
1432 * Extract public port and optionally address.
1433 */
1434 ptr = strtok (NULL, " \t");
1435 if (!ptr)
1436 errx (1, "redirect_port: missing public port");
1437
1438 separator = strchr (ptr, ':');
1439 if (separator) {
1440 if (StrToAddrAndPortRange (ptr, &publicAddr, protoName, &portRange) != 0 )
1441 errx (1, "redirect_port: invalid public port range");
1442 }
1443 else {
1444 publicAddr.s_addr = INADDR_ANY;
1445 if (StrToPortRange (ptr, protoName, &portRange) != 0)
1446 errx (1, "redirect_port: invalid public port range");
1447 }
1448
1449 publicPort = GETLOPORT(portRange);
1450 numPublicPorts = GETNUMPORTS(portRange);
1451
1452 /*
1453 * Extract remote address and optionally port.
1454 */
1455 ptr = strtok (NULL, " \t");
1456 if (ptr) {
1457 separator = strchr (ptr, ':');
1458 if (separator) {
1459 if (StrToAddrAndPortRange (ptr, &remoteAddr, protoName, &portRange) != 0)
1460 errx (1, "redirect_port: invalid remote port range");
1461 } else {
1462 SETLOPORT(portRange, 0);
1463 SETNUMPORTS(portRange, 1);
1464 StrToAddr (ptr, &remoteAddr);
1465 }
1466 }
1467 else {
1468 SETLOPORT(portRange, 0);
1469 SETNUMPORTS(portRange, 1);
1470 remoteAddr.s_addr = INADDR_ANY;
1471 }
1472
1473 remotePort = GETLOPORT(portRange);
1474 numRemotePorts = GETNUMPORTS(portRange);
1475
1476 /*
1477 * Make sure port ranges match up, then add the redirect ports.
1478 */
1479 if (numLocalPorts != numPublicPorts)
1480 errx (1, "redirect_port: port ranges must be equal in size");
1481
1482 /* Remote port range is allowed to be '0' which means all ports. */
1483 if (numRemotePorts != numLocalPorts && (numRemotePorts != 1 || remotePort != 0))
1484 errx (1, "redirect_port: remote port must be 0 or equal to local port range in size");
1485
1486 for (i = 0 ; i < numPublicPorts ; ++i) {
1487 /* If remotePort is all ports, set it to 0. */
1488 u_short remotePortCopy = remotePort + i;
1489 if (numRemotePorts == 1 && remotePort == 0)
1490 remotePortCopy = 0;
1491
1492 link = PacketAliasRedirectPort (localAddr,
1493 htons(localPort + i),
1494 remoteAddr,
1495 htons(remotePortCopy),
1496 publicAddr,
1497 htons(publicPort + i),
1498 proto);
1499 }
1500
1501 /*
1502 * Setup LSNAT server pool.
1503 */
1504 if (serverPool != NULL && link != NULL) {
1505 ptr = strtok(serverPool, ",");
1506 while (ptr != NULL) {
1507 if (StrToAddrAndPortRange(ptr, &localAddr, protoName, &portRange) != 0)
1508 errx(1, "redirect_port: invalid local port range");
1509
1510 localPort = GETLOPORT(portRange);
1511 if (GETNUMPORTS(portRange) != 1)
1512 errx(1, "redirect_port: local port must be single in this context");
1513 PacketAliasAddServer(link, localAddr, htons(localPort));
1514 ptr = strtok(NULL, ",");
1515 }
1516 }
1517 }
1518
1519 void
1520 SetupProtoRedirect(const char* parms)
1521 {
1522 char buf[128];
1523 char* ptr;
1524 struct in_addr localAddr;
1525 struct in_addr publicAddr;
1526 struct in_addr remoteAddr;
1527 int proto;
1528 char* protoName;
1529 struct protoent *protoent;
1530
1531 strcpy (buf, parms);
1532 /*
1533 * Extract protocol.
1534 */
1535 protoName = strtok(buf, " \t");
1536 if (!protoName)
1537 errx(1, "redirect_proto: missing protocol");
1538
1539 protoent = getprotobyname(protoName);
1540 if (protoent == NULL)
1541 errx(1, "redirect_proto: unknown protocol %s", protoName);
1542 else
1543 proto = protoent->p_proto;
1544 /*
1545 * Extract local address.
1546 */
1547 ptr = strtok(NULL, " \t");
1548 if (!ptr)
1549 errx(1, "redirect_proto: missing local address");
1550 else
1551 StrToAddr(ptr, &localAddr);
1552 /*
1553 * Extract optional public address.
1554 */
1555 ptr = strtok(NULL, " \t");
1556 if (ptr)
1557 StrToAddr(ptr, &publicAddr);
1558 else
1559 publicAddr.s_addr = INADDR_ANY;
1560 /*
1561 * Extract optional remote address.
1562 */
1563 ptr = strtok(NULL, " \t");
1564 if (ptr)
1565 StrToAddr(ptr, &remoteAddr);
1566 else
1567 remoteAddr.s_addr = INADDR_ANY;
1568 /*
1569 * Create aliasing link.
1570 */
1571 (void)PacketAliasRedirectProto(localAddr, remoteAddr, publicAddr,
1572 proto);
1573 }
1574
1575 void SetupAddressRedirect (const char* parms)
1576 {
1577 char buf[128];
1578 char* ptr;
1579 char* separator;
1580 struct in_addr localAddr;
1581 struct in_addr publicAddr;
1582 char* serverPool;
1583 struct alias_link *link;
1584
1585 strcpy (buf, parms);
1586 /*
1587 * Extract local address.
1588 */
1589 ptr = strtok (buf, " \t");
1590 if (!ptr)
1591 errx (1, "redirect_address: missing local address");
1592
1593 separator = strchr(ptr, ',');
1594 if (separator) { /* LSNAT redirection syntax. */
1595 localAddr.s_addr = INADDR_NONE;
1596 serverPool = ptr;
1597 } else {
1598 StrToAddr (ptr, &localAddr);
1599 serverPool = NULL;
1600 }
1601 /*
1602 * Extract public address.
1603 */
1604 ptr = strtok (NULL, " \t");
1605 if (!ptr)
1606 errx (1, "redirect_address: missing public address");
1607
1608 StrToAddr (ptr, &publicAddr);
1609 link = PacketAliasRedirectAddr(localAddr, publicAddr);
1610
1611 /*
1612 * Setup LSNAT server pool.
1613 */
1614 if (serverPool != NULL && link != NULL) {
1615 ptr = strtok(serverPool, ",");
1616 while (ptr != NULL) {
1617 StrToAddr(ptr, &localAddr);
1618 PacketAliasAddServer(link, localAddr, htons(~0));
1619 ptr = strtok(NULL, ",");
1620 }
1621 }
1622 }
1623
1624 void StrToAddr (const char* str, struct in_addr* addr)
1625 {
1626 struct hostent* hp;
1627
1628 if (inet_aton (str, addr))
1629 return;
1630
1631 hp = gethostbyname (str);
1632 if (!hp)
1633 errx (1, "unknown host %s", str);
1634
1635 memcpy (addr, hp->h_addr, sizeof (struct in_addr));
1636 }
1637
1638 u_short StrToPort (const char* str, const char* proto)
1639 {
1640 u_short port;
1641 struct servent* sp;
1642 char* end;
1643
1644 port = strtol (str, &end, 10);
1645 if (end != str)
1646 return htons (port);
1647
1648 sp = getservbyname (str, proto);
1649 if (!sp)
1650 errx (1, "unknown service %s/%s", str, proto);
1651
1652 return sp->s_port;
1653 }
1654
1655 int StrToPortRange (const char* str, const char* proto, port_range *portRange)
1656 {
1657 char* sep;
1658 struct servent* sp;
1659 char* end;
1660 u_short loPort;
1661 u_short hiPort;
1662
1663 /* First see if this is a service, return corresponding port if so. */
1664 sp = getservbyname (str,proto);
1665 if (sp) {
1666 SETLOPORT(*portRange, ntohs(sp->s_port));
1667 SETNUMPORTS(*portRange, 1);
1668 return 0;
1669 }
1670
1671 /* Not a service, see if it's a single port or port range. */
1672 sep = strchr (str, '-');
1673 if (sep == NULL) {
1674 SETLOPORT(*portRange, strtol(str, &end, 10));
1675 if (end != str) {
1676 /* Single port. */
1677 SETNUMPORTS(*portRange, 1);
1678 return 0;
1679 }
1680
1681 /* Error in port range field. */
1682 errx (1, "unknown service %s/%s", str, proto);
1683 }
1684
1685 /* Port range, get the values and sanity check. */
1686 sscanf (str, "%hu-%hu", &loPort, &hiPort);
1687 SETLOPORT(*portRange, loPort);
1688 SETNUMPORTS(*portRange, 0); /* Error by default */
1689 if (loPort <= hiPort)
1690 SETNUMPORTS(*portRange, hiPort - loPort + 1);
1691
1692 if (GETNUMPORTS(*portRange) == 0)
1693 errx (1, "invalid port range %s", str);
1694
1695 return 0;
1696 }
1697
1698
1699 int StrToProto (const char* str)
1700 {
1701 if (!strcmp (str, "tcp"))
1702 return IPPROTO_TCP;
1703
1704 if (!strcmp (str, "udp"))
1705 return IPPROTO_UDP;
1706
1707 errx (1, "unknown protocol %s. Expected tcp or udp", str);
1708 }
1709
1710 int StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto, port_range *portRange)
1711 {
1712 char* ptr;
1713
1714 ptr = strchr (str, ':');
1715 if (!ptr)
1716 errx (1, "%s is missing port number", str);
1717
1718 *ptr = '\0';
1719 ++ptr;
1720
1721 StrToAddr (str, addr);
1722 return StrToPortRange (ptr, proto, portRange);
1723 }
1724
1725 static void
1726 SetupPunchFW(const char *strValue)
1727 {
1728 unsigned int base, num;
1729
1730 if (sscanf(strValue, "%u:%u", &base, &num) != 2)
1731 errx(1, "punch_fw: basenumber:count parameter required");
1732
1733 PacketAliasSetFWBase(base, num);
1734 (void)PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
1735 }