]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ipfilter/l4check/l4check.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / ipfilter / l4check / l4check.c
1 /*      $FreeBSD$       */
2
3 /*
4  * (C)Copyright (C) 2012 by Darren Reed.
5  */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/mman.h>
9 #include <sys/socket.h>
10 #include <sys/time.h>
11 #include <sys/ioctl.h>
12
13 #include <netinet/in.h>
14 #include <netinet/in_systm.h>
15 #include <netinet/ip.h>
16
17 #include <net/if.h>
18
19 #include <stdio.h>
20 #include <netdb.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <stdlib.h>
26
27 #include "ip_compat.h"
28 #include "ip_fil.h"
29 #include "ip_nat.h"
30
31 #include "ipf.h"
32
33 extern  char    *optarg;
34
35
36 typedef struct  l4cfg   {
37         struct  l4cfg           *l4_next;
38         struct  ipnat           l4_nat;         /* NAT rule */
39         struct  sockaddr_in     l4_sin;         /* remote socket to connect */
40         time_t                  l4_last;        /* when we last connected */
41         int                     l4_alive;       /* 1 = remote alive */
42         int                     l4_fd;
43         int                     l4_rw;          /* 0 = reading, 1 = writing */
44         char                    *l4_rbuf;       /* read buffer */
45         int                     l4_rsize;       /* size of buffer */
46         int                     l4_rlen;        /* how much used */
47         char                    *l4_wptr;       /* next byte to write */
48         int                     l4_wlen;        /* length yet to be written */
49 } l4cfg_t;
50
51
52 l4cfg_t *l4list = NULL;
53 char *response = NULL;
54 char *probe = NULL;
55 l4cfg_t template;
56 int frequency = 20;
57 int ctimeout = 1;
58 int rtimeout = 1;
59 size_t plen = 0;
60 size_t rlen = 0;
61 int natfd = -1;
62 int opts = 0;
63
64 #if defined(sun) && !defined(__svr4__) && !defined(__SVR4)
65 # define        strerror(x)     sys_errlist[x]
66 #endif
67
68
69 char *copystr(dst, src)
70         char *dst, *src;
71 {
72         register char *s, *t, c;
73         register int esc = 0;
74
75         for (s = src, t = dst; s && t && (c = *s++); )
76                 if (esc) {
77                         esc = 0;
78                         switch (c)
79                         {
80                         case 'n' :
81                                 *t++ = '\n';
82                                 break;
83                         case 'r' :
84                                 *t++ = '\r';
85                                 break;
86                         case 't' :
87                                 *t++ = '\t';
88                                 break;
89                         }
90                 } else if (c != '\\')
91                         *t++ = c;
92                 else
93                         esc = 1;
94         *t = '\0';
95         return dst;
96 }
97
98 void addnat(l4)
99         l4cfg_t *l4;
100 {
101         ipnat_t *ipn = &l4->l4_nat;
102
103         printf("Add NAT rule for %s/%#x,%u -> ", inet_ntoa(ipn->in_out[0]),
104                 ipn->in_outmsk, ntohs(ipn->in_pmin));
105         printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ntohs(ipn->in_pnext));
106         if (!(opts & OPT_DONOTHING)) {
107                 if (ioctl(natfd, SIOCADNAT, &ipn) == -1)
108                         perror("ioctl(SIOCADNAT)");
109         }
110 }
111
112
113 void delnat(l4)
114         l4cfg_t *l4;
115 {
116         ipnat_t *ipn = &l4->l4_nat;
117
118         printf("Remove NAT rule for %s/%#x,%u -> ",
119                 inet_ntoa(ipn->in_out[0]), ipn->in_outmsk, ipn->in_pmin);
120         printf("%s,%u\n", inet_ntoa(ipn->in_in[0]), ipn->in_pnext);
121         if (!(opts & OPT_DONOTHING)) {
122                 if (ioctl(natfd, SIOCRMNAT, &ipn) == -1)
123                         perror("ioctl(SIOCRMNAT)");
124         }
125 }
126
127
128 void connectl4(l4)
129         l4cfg_t *l4;
130 {
131         l4->l4_rw = 1;
132         l4->l4_rlen = 0;
133         l4->l4_wlen = plen;
134         if (!l4->l4_wlen) {
135                 l4->l4_alive = 1;
136                 addnat(l4);
137         } else
138                 l4->l4_wptr = probe;
139 }
140
141
142 void closel4(l4, dead)
143         l4cfg_t *l4;
144         int dead;
145 {
146         close(l4->l4_fd);
147         l4->l4_fd = -1;
148         l4->l4_rw = -1;
149         if (dead && l4->l4_alive) {
150                 l4->l4_alive = 0;
151                 delnat(l4);
152         }
153 }
154
155
156 void connectfd(l4)
157         l4cfg_t *l4;
158 {
159         if (connect(l4->l4_fd, (struct sockaddr *)&l4->l4_sin,
160                     sizeof(l4->l4_sin)) == -1) {
161                 if (errno == EISCONN) {
162                         if (opts & OPT_VERBOSE)
163                                 fprintf(stderr, "Connected fd %d\n",
164                                         l4->l4_fd);
165                         connectl4(l4);
166                         return;
167                 }
168                 if (opts & OPT_VERBOSE)
169                         fprintf(stderr, "Connect failed fd %d: %s\n",
170                                 l4->l4_fd, strerror(errno));
171                 closel4(l4, 1);
172                 return;
173         }
174         l4->l4_rw = 1;
175 }
176
177
178 void writefd(l4)
179         l4cfg_t *l4;
180 {
181         char buf[80], *ptr;
182         int n, i, fd;
183
184         fd = l4->l4_fd;
185
186         if (l4->l4_rw == -2) {
187                 connectfd(l4);
188                 return;
189         }
190
191         n = l4->l4_wlen;
192
193         i = send(fd, l4->l4_wptr, n, 0);
194         if (i == 0 || i == -1) {
195                 if (opts & OPT_VERBOSE)
196                         fprintf(stderr, "Send on fd %d failed: %s\n",
197                                 fd, strerror(errno));
198                 closel4(l4, 1);
199         } else {
200                 l4->l4_wptr += i;
201                 l4->l4_wlen -= i;
202                 if (l4->l4_wlen == 0)
203                         l4->l4_rw = 0;
204                 if (opts & OPT_VERBOSE)
205                         fprintf(stderr, "Sent %d bytes to fd %d\n", i, fd);
206         }
207 }
208
209
210 void readfd(l4)
211         l4cfg_t *l4;
212 {
213         char buf[80], *ptr;
214         int n, i, fd;
215
216         fd = l4->l4_fd;
217
218         if (l4->l4_rw == -2) {
219                 connectfd(l4);
220                 return;
221         }
222
223         if (l4->l4_rsize) {
224                 n = l4->l4_rsize - l4->l4_rlen;
225                 ptr = l4->l4_rbuf + l4->l4_rlen;
226         } else {
227                 n = sizeof(buf) - 1;
228                 ptr = buf;
229         }
230
231         if (opts & OPT_VERBOSE)
232                 fprintf(stderr, "Read %d bytes on fd %d to %p\n",
233                         n, fd, ptr);
234         i = recv(fd, ptr, n, 0);
235         if (i == 0 || i == -1) {
236                 if (opts & OPT_VERBOSE)
237                         fprintf(stderr, "Read error on fd %d: %s\n",
238                                 fd, (i == 0) ? "EOF" : strerror(errno));
239                 closel4(l4, 1);
240         } else {
241                 if (ptr == buf)
242                         ptr[i] = '\0';
243                 if (opts & OPT_VERBOSE)
244                         fprintf(stderr, "%d: Read %d bytes [%*.*s]\n",
245                                 fd, i, i, i, ptr);
246                 if (ptr != buf) {
247                         l4->l4_rlen += i;
248                         if (l4->l4_rlen >= l4->l4_rsize) {
249                                 if (!strncmp(response, l4->l4_rbuf,
250                                              l4->l4_rsize)) {
251                                         printf("%d: Good response\n",
252                                                 fd);
253                                         if (!l4->l4_alive) {
254                                                 l4->l4_alive = 1;
255                                                 addnat(l4);
256                                         }
257                                         closel4(l4, 0);
258                                 } else {
259                                         if (opts & OPT_VERBOSE)
260                                                 printf("%d: Bad response\n",
261                                                         fd);
262                                         closel4(l4, 1);
263                                 }
264                         }
265                 } else if (!l4->l4_alive) {
266                         l4->l4_alive = 1;
267                         addnat(l4);
268                         closel4(l4, 0);
269                 }
270         }
271 }
272
273
274 int runconfig()
275 {
276         int fd, opt, res, mfd, i;
277         struct timeval tv;
278         time_t now, now1;
279         fd_set rfd, wfd;
280         l4cfg_t *l4;
281
282         mfd = 0;
283         opt = 1;
284         now = time(NULL);
285
286         /*
287          * First, initiate connections that are closed, as required.
288          */
289         for (l4 = l4list; l4; l4 = l4->l4_next) {
290                 if ((l4->l4_last + frequency < now) && (l4->l4_fd == -1)) {
291                         l4->l4_last = now;
292                         fd = socket(AF_INET, SOCK_STREAM, 0);
293                         if (fd == -1)
294                                 continue;
295                         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
296                                    sizeof(opt));
297 #ifdef  O_NONBLOCK
298                         if ((res = fcntl(fd, F_GETFL, 0)) != -1)
299                                 fcntl(fd, F_SETFL, res | O_NONBLOCK);
300 #endif
301                         if (opts & OPT_VERBOSE)
302                                 fprintf(stderr,
303                                         "Connecting to %s,%d (fd %d)...",
304                                         inet_ntoa(l4->l4_sin.sin_addr),
305                                         ntohs(l4->l4_sin.sin_port), fd);
306                         if (connect(fd, (struct sockaddr *)&l4->l4_sin,
307                                     sizeof(l4->l4_sin)) == -1) {
308                                 if (errno != EINPROGRESS) {
309                                         if (opts & OPT_VERBOSE)
310                                                 fprintf(stderr, "failed\n");
311                                         perror("connect");
312                                         close(fd);
313                                         fd = -1;
314                                 } else {
315                                         if (opts & OPT_VERBOSE)
316                                                 fprintf(stderr, "waiting\n");
317                                         l4->l4_rw = -2;
318                                 }
319                         } else {
320                                 if (opts & OPT_VERBOSE)
321                                         fprintf(stderr, "connected\n");
322                                 connectl4(l4);
323                         }
324                         l4->l4_fd = fd;
325                 }
326         }
327
328         /*
329          * Now look for fd's which we're expecting to read/write from.
330          */
331         FD_ZERO(&rfd);
332         FD_ZERO(&wfd);
333         tv.tv_sec = MIN(rtimeout, ctimeout);
334         tv.tv_usec = 0;
335
336         for (l4 = l4list; l4; l4 = l4->l4_next)
337                 if (l4->l4_rw == 0) {
338                         if (now - l4->l4_last > rtimeout) {
339                                 if (opts & OPT_VERBOSE)
340                                         fprintf(stderr, "%d: Read timeout\n",
341                                                 l4->l4_fd);
342                                 closel4(l4, 1);
343                                 continue;
344                         }
345                         if (opts & OPT_VERBOSE)
346                                 fprintf(stderr, "Wait for read on fd %d\n",
347                                         l4->l4_fd);
348                         FD_SET(l4->l4_fd, &rfd);
349                         if (l4->l4_fd > mfd)
350                                 mfd = l4->l4_fd;
351                 } else if ((l4->l4_rw == 1 && l4->l4_wlen) ||
352                            l4->l4_rw == -2) {
353                         if ((l4->l4_rw == -2) &&
354                             (now - l4->l4_last > ctimeout)) {
355                                 if (opts & OPT_VERBOSE)
356                                         fprintf(stderr,
357                                                 "%d: connect timeout\n",
358                                                 l4->l4_fd);
359                                 closel4(l4);
360                                 continue;
361                         }
362                         if (opts & OPT_VERBOSE)
363                                 fprintf(stderr, "Wait for write on fd %d\n",
364                                         l4->l4_fd);
365                         FD_SET(l4->l4_fd, &wfd);
366                         if (l4->l4_fd > mfd)
367                                 mfd = l4->l4_fd;
368                 }
369
370         if (opts & OPT_VERBOSE)
371                 fprintf(stderr, "Select: max fd %d wait %d\n", mfd + 1,
372                         tv.tv_sec);
373         i = select(mfd + 1, &rfd, &wfd, NULL, &tv);
374         if (i == -1) {
375                 perror("select");
376                 return -1;
377         }
378
379         now1 = time(NULL);
380
381         for (l4 = l4list; (i > 0) && l4; l4 = l4->l4_next) {
382                 if (l4->l4_fd < 0)
383                         continue;
384                 if (FD_ISSET(l4->l4_fd, &rfd)) {
385                         if (opts & OPT_VERBOSE)
386                                 fprintf(stderr, "Ready to read on fd %d\n",
387                                         l4->l4_fd);
388                         readfd(l4);
389                         i--;
390                 }
391
392                 if ((l4->l4_fd >= 0) && FD_ISSET(l4->l4_fd, &wfd)) {
393                         if (opts & OPT_VERBOSE)
394                                 fprintf(stderr, "Ready to write on fd %d\n",
395                                         l4->l4_fd);
396                         writefd(l4);
397                         i--;
398                 }
399         }
400         return 0;
401 }
402
403
404 int gethostport(str, lnum, ipp, portp)
405         char *str;
406         int lnum;
407         u_32_t *ipp;
408         u_short *portp;
409 {
410         struct servent *sp;
411         struct hostent *hp;
412         char *host, *port;
413         struct in_addr ip;
414
415         host = str;
416         port = strchr(host, ',');
417         if (port)
418                 *port++ = '\0';
419
420 #ifdef  HAVE_INET_ATON
421         if (ISDIGIT(*host) && inet_aton(host, &ip))
422                 *ipp = ip.s_addr;
423 #else
424         if (ISDIGIT(*host))
425                 *ipp = inet_addr(host);
426 #endif
427         else {
428                 if (!(hp = gethostbyname(host))) {
429                         fprintf(stderr, "%d: can't resolve hostname: %s\n",
430                                 lnum, host);
431                         return 0;
432                 }
433                 *ipp = *(u_32_t *)hp->h_addr;
434         }
435
436         if (port) {
437                 if (ISDIGIT(*port))
438                         *portp = htons(atoi(port));
439                 else {
440                         sp = getservbyname(port, "tcp");
441                         if (sp)
442                                 *portp = sp->s_port;
443                         else {
444                                 fprintf(stderr, "%d: unknown service %s\n",
445                                         lnum, port);
446                                 return 0;
447                         }
448                 }
449         } else
450                 *portp = 0;
451         return 1;
452 }
453
454
455 char *mapfile(file, sizep)
456         char *file;
457         size_t *sizep;
458 {
459         struct stat sb;
460         caddr_t addr;
461         int fd;
462
463         fd = open(file, O_RDONLY);
464         if (fd == -1) {
465                 perror("open(mapfile)");
466                 return NULL;
467         }
468
469         if (fstat(fd, &sb) == -1) {
470                 perror("fstat(mapfile)");
471                 close(fd);
472                 return NULL;
473         }
474
475         addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
476         if (addr == (caddr_t)-1) {
477                 perror("mmap(mapfile)");
478                 close(fd);
479                 return NULL;
480         }
481         close(fd);
482         *sizep = sb.st_size;
483         return (char *)addr;
484 }
485
486
487 int readconfig(filename)
488         char *filename;
489 {
490         char c, buf[512], *s, *t, *errtxt = NULL, *line;
491         int num, err = 0;
492         ipnat_t *ipn;
493         l4cfg_t *l4;
494         FILE *fp;
495
496         fp = fopen(filename, "r");
497         if (!fp) {
498                 perror("open(configfile)");
499                 return -1;
500         }
501
502         bzero((char *)&template, sizeof(template));
503         template.l4_fd = -1;
504         template.l4_rw = -1;
505         template.l4_sin.sin_family = AF_INET;
506         ipn = &template.l4_nat;
507         ipn->in_flags = IPN_TCP|IPN_ROUNDR;
508         ipn->in_redir = NAT_REDIRECT;
509
510         for (num = 1; fgets(buf, sizeof(buf), fp); num++) {
511                 s = strchr(buf, '\n');
512                 if  (!s) {
513                         fprintf(stderr, "%d: line too long\n", num);
514                         fclose(fp);
515                         return -1;
516                 }
517
518                 *s = '\0';
519
520                 /*
521                  * lines which are comments
522                  */
523                 s = strchr(buf, '#');
524                 if (s)
525                         *s = '\0';
526
527                 /*
528                  * Skip leading whitespace
529                  */
530                 for (line = buf; (c = *line) && ISSPACE(c); line++)
531                         ;
532                 if (!*line)
533                         continue;
534
535                 if (opts & OPT_VERBOSE)
536                         fprintf(stderr, "Parsing: [%s]\n", line);
537                 t = strtok(line, " \t");
538                 if (!t)
539                         continue;
540                 if (!strcasecmp(t, "interface")) {
541                         s = strtok(NULL, " \t");
542                         if (s)
543                                 t = strtok(NULL, "\t");
544                         if (!s || !t) {
545                                 errtxt = line;
546                                 err = -1;
547                                 break;
548                         }
549
550                         if (!strchr(t, ',')) {
551                                 fprintf(stderr,
552                                         "%d: local address,port missing\n",
553                                         num);
554                                 err = -1;
555                                 break;
556                         }
557
558                         strncpy(ipn->in_ifname, s, sizeof(ipn->in_ifname));
559                         if (!gethostport(t, num, &ipn->in_outip,
560                                          &ipn->in_pmin)) {
561                                 errtxt = line;
562                                 err = -1;
563                                 break;
564                         }
565                         ipn->in_outmsk = 0xffffffff;
566                         ipn->in_pmax = ipn->in_pmin;
567                         if (opts & OPT_VERBOSE)
568                                 fprintf(stderr,
569                                         "Interface %s %s/%#x port %u\n",
570                                         ipn->in_ifname,
571                                         inet_ntoa(ipn->in_out[0]),
572                                         ipn->in_outmsk, ipn->in_pmin);
573                 } else if (!strcasecmp(t, "remote")) {
574                         if (!*ipn->in_ifname) {
575                                 fprintf(stderr,
576                                         "%d: ifname not set prior to remote\n",
577                                         num);
578                                 err = -1;
579                                 break;
580                         }
581                         s = strtok(NULL, " \t");
582                         if (s)
583                                 t = strtok(NULL, "");
584                         if (!s || !t || strcasecmp(s, "server")) {
585                                 errtxt = line;
586                                 err = -1;
587                                 break;
588                         }
589
590                         ipn->in_pnext = 0;
591                         if (!gethostport(t, num, &ipn->in_inip,
592                                          &ipn->in_pnext)) {
593                                 errtxt = line;
594                                 err = -1;
595                                 break;
596                         }
597                         ipn->in_inmsk = 0xffffffff;
598                         if (ipn->in_pnext == 0)
599                                 ipn->in_pnext = ipn->in_pmin;
600
601                         l4 = (l4cfg_t *)malloc(sizeof(*l4));
602                         if (!l4) {
603                                 fprintf(stderr, "%d: out of memory (%d)\n",
604                                         num, sizeof(*l4));
605                                 err = -1;
606                                 break;
607                         }
608                         bcopy((char *)&template, (char *)l4, sizeof(*l4));
609                         l4->l4_sin.sin_addr = ipn->in_in[0];
610                         l4->l4_sin.sin_port = ipn->in_pnext;
611                         l4->l4_next = l4list;
612                         l4list = l4;
613                 } else if (!strcasecmp(t, "connect")) {
614                         s = strtok(NULL, " \t");
615                         if (s)
616                                 t = strtok(NULL, "\t");
617                         if (!s || !t) {
618                                 errtxt = line;
619                                 err = -1;
620                                 break;
621                         } else if (!strcasecmp(s, "timeout")) {
622                                 ctimeout = atoi(t);
623                                 if (opts & OPT_VERBOSE)
624                                         fprintf(stderr, "connect timeout %d\n",
625                                                 ctimeout);
626                         } else if (!strcasecmp(s, "frequency")) {
627                                 frequency = atoi(t);
628                                 if (opts & OPT_VERBOSE)
629                                         fprintf(stderr,
630                                                 "connect frequency %d\n",
631                                                 frequency);
632                         } else {
633                                 errtxt = line;
634                                 err = -1;
635                                 break;
636                         }
637                 } else if (!strcasecmp(t, "probe")) {
638                         s = strtok(NULL, " \t");
639                         if (!s) {
640                                 errtxt = line;
641                                 err = -1;
642                                 break;
643                         } else if (!strcasecmp(s, "string")) {
644                                 if (probe) {
645                                         fprintf(stderr,
646                                                 "%d: probe already set\n",
647                                                 num);
648                                         err = -1;
649                                         break;
650                                 }
651                                 t = strtok(NULL, "");
652                                 if (!t) {
653                                         fprintf(stderr,
654                                                 "%d: No probe string\n", num);
655                                         err = -1;
656                                         break;
657                                 }
658
659                                 probe = malloc(strlen(t));
660                                 copystr(probe, t);
661                                 plen = strlen(probe);
662                                 if (opts & OPT_VERBOSE)
663                                         fprintf(stderr, "Probe string [%s]\n",
664                                                 probe);
665                         } else if (!strcasecmp(s, "file")) {
666                                 t = strtok(NULL, " \t");
667                                 if (!t) {
668                                         errtxt = line;
669                                         err = -1;
670                                         break;
671                                 }
672                                 if (probe) {
673                                         fprintf(stderr,
674                                                 "%d: probe already set\n",
675                                                 num);
676                                         err = -1;
677                                         break;
678                                 }
679                                 probe = mapfile(t, &plen);
680                                 if (opts & OPT_VERBOSE)
681                                         fprintf(stderr,
682                                                 "Probe file %s len %u@%p\n",
683                                                 t, plen, probe);
684                         }
685                 } else if (!strcasecmp(t, "response")) {
686                         s = strtok(NULL, " \t");
687                         if (!s) {
688                                 errtxt = line;
689                                 err = -1;
690                                 break;
691                         } else if (!strcasecmp(s, "timeout")) {
692                                 t = strtok(NULL, " \t");
693                                 if (!t) {
694                                         errtxt = line;
695                                         err = -1;
696                                         break;
697                                 }
698                                 rtimeout = atoi(t);
699                                 if (opts & OPT_VERBOSE)
700                                         fprintf(stderr,
701                                                 "response timeout %d\n",
702                                                 rtimeout);
703                         } else if (!strcasecmp(s, "string")) {
704                                 if (response) {
705                                         fprintf(stderr,
706                                                 "%d: response already set\n",
707                                                 num);
708                                         err = -1;
709                                         break;
710                                 }
711                                 response = strdup(strtok(NULL, ""));
712                                 rlen = strlen(response);
713                                 template.l4_rsize = rlen;
714                                 template.l4_rbuf = malloc(rlen);
715                                 if (opts & OPT_VERBOSE)
716                                         fprintf(stderr,
717                                                 "Response string [%s]\n",
718                                                 response);
719                         } else if (!strcasecmp(s, "file")) {
720                                 t = strtok(NULL, " \t");
721                                 if (!t) {
722                                         errtxt = line;
723                                         err = -1;
724                                         break;
725                                 }
726                                 if (response) {
727                                         fprintf(stderr,
728                                                 "%d: response already set\n",
729                                                 num);
730                                         err = -1;
731                                         break;
732                                 }
733                                 response = mapfile(t, &rlen);
734                                 template.l4_rsize = rlen;
735                                 template.l4_rbuf = malloc(rlen);
736                                 if (opts & OPT_VERBOSE)
737                                         fprintf(stderr,
738                                                 "Response file %s len %u@%p\n",
739                                                 t, rlen, response);
740                         }
741                 } else {
742                         errtxt = line;
743                         err = -1;
744                         break;
745                 }
746         }
747
748         if (errtxt)
749                 fprintf(stderr, "%d: syntax error at \"%s\"\n", num, errtxt);
750         fclose(fp);
751         return err;
752 }
753
754
755 void usage(prog)
756         char *prog;
757 {
758         fprintf(stderr, "Usage: %s -f <configfile>\n", prog);
759         exit(1);
760 }
761
762
763 int main(argc, argv)
764         int argc;
765         char *argv[];
766 {
767         char *config = NULL;
768         int c;
769
770         while ((c = getopt(argc, argv, "f:nv")) != -1)
771                 switch (c)
772                 {
773                 case 'f' :
774                         config = optarg;
775                         break;
776                 case 'n' :
777                         opts |= OPT_DONOTHING;
778                         break;
779                 case 'v' :
780                         opts |= OPT_VERBOSE;
781                         break;
782                 }
783
784         if (config == NULL)
785                 usage(argv[0]);
786
787         if (readconfig(config))
788                 exit(1);
789
790         if (!l4list) {
791                 fprintf(stderr, "No remote servers, exiting.");
792                 exit(1);
793         }
794
795         if (!(opts & OPT_DONOTHING)) {
796                 natfd = open(IPL_NAT, O_RDWR);
797                 if (natfd == -1) {
798                         perror("open(IPL_NAT)");
799                         exit(1);
800                 }
801         }
802
803         if (opts & OPT_VERBOSE)
804                 fprintf(stderr, "Starting...\n");
805         while (runconfig() == 0)
806                 ;
807 }