Merge from vendor branch OPENSSL:
[dragonfly.git] / contrib / ipfilter / ipt.c
1 /*
2  * Copyright (C) 1993-2002 by Darren Reed.
3  *
4  * See the IPFILTER.LICENCE file for details on licencing.
5  */
6 #ifdef  __FreeBSD__
7 # ifndef __FreeBSD_cc_version
8 #  include <osreldate.h>
9 # else
10 #  if __FreeBSD_cc_version < 430000
11 #   include <osreldate.h>
12 #  endif
13 # endif
14 #endif
15 #if defined(__sgi) && (IRIX > 602)
16 # define _KMEMUSER
17 # include <sys/ptimers.h>
18 #endif
19 #include <stdio.h>
20 #include <assert.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #if !defined(__SVR4) && !defined(__svr4__) && !defined(__sgi)
24 #include <strings.h>
25 #else
26 #if !defined(__sgi)
27 #include <sys/byteorder.h>
28 #endif
29 #include <sys/file.h>
30 #endif
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <stddef.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <netinet/in.h>
39 #include <netinet/in_systm.h>
40 #ifndef linux
41 #include <netinet/ip_var.h>
42 #endif
43 #include <netinet/ip.h>
44 #include <netinet/udp.h>
45 #include <netinet/tcp.h>
46 #include <netinet/ip_icmp.h>
47 #include <net/if.h>
48 #if __FreeBSD_version >= 300000
49 # include <net/if_var.h>
50 #endif
51 #include <netdb.h>
52 #include <arpa/nameser.h>
53 #include <arpa/inet.h>
54 #include <resolv.h>
55 #include <ctype.h>
56 #include "ip_compat.h"
57 #include <netinet/tcpip.h>
58 #include "ip_fil.h"
59 #include "ip_nat.h"
60 #include "ip_state.h"
61 #include "ip_frag.h"
62 #include "ipf.h"
63 #include "ipt.h"
64
65 #if !defined(lint)
66 static const char sccsid[] = "@(#)ipt.c 1.19 6/3/96 (C) 1993-2000 Darren Reed";
67 static const char rcsid[] = "@(#)$Id: ipt.c,v 2.6.2.26 2003/11/09 17:22:21 darrenr Exp $";
68 #endif
69
70 extern  char    *optarg;
71 extern  struct frentry  *ipfilter[2][2];
72 extern  struct ipread   snoop, etherf, tcpd, pcap, iptext, iphex;
73 extern  struct ifnet    *get_unit __P((char *, int));
74 extern  void    init_ifp __P((void));
75 extern  ipnat_t *natparse __P((char *, int, int *));
76 extern  int     fr_running;
77
78 int     opts = 0;
79 int     rremove = 0;
80 int     use_inet6 = 0;
81 int     main __P((int, char *[]));
82 int     loadrules __P((char *));
83 int     kmemcpy __P((char *, long, int));
84 void    dumpnat __P((void));
85 void    dumpstate __P((void));
86 char    *getifname __P((void *));
87 void    drain_log __P((char *));
88
89 int main(argc,argv)
90 int argc;
91 char *argv[];
92 {
93         char    *datain, *iface, *ifname, *packet, *logout;
94         int     fd, i, dir, c, loaded, dump, hlen;
95         struct  in_addr src;
96         struct  ifnet   *ifp;
97         struct  ipread  *r;
98         u_long  buf[2048];
99         ip_t    *ip;
100
101         dir = 0;
102         dump = 0;
103         loaded = 0;
104         r = &iptext;
105         iface = NULL;
106         logout = NULL;
107         src.s_addr = 0;
108         ifname = "anon0";
109         datain = NULL;
110
111         nat_init();
112         fr_stateinit();
113         initparse();
114         ipflog_init();
115         fr_running = 1;
116
117         while ((c = getopt(argc, argv, "6bdDEHi:I:l:NoPr:Rs:STvxX")) != -1)
118                 switch (c)
119                 {
120                 case '6' :
121 #ifdef  USE_INET6
122                         use_inet6 = 1;
123                         break;
124 #else
125                         fprintf(stderr, "IPv6 not supported\n");
126                         exit(1);
127 #endif
128                 case 'b' :
129                         opts |= OPT_BRIEF;
130                         break;
131                 case 'd' :
132                         opts |= OPT_DEBUG;
133                         break;
134                 case 'D' :
135                         dump = 1;
136                         break;
137                 case 'i' :
138                         datain = optarg;
139                         break;
140                 case 'I' :
141                         ifname = optarg;
142                         break;
143                 case 'l' :
144                         logout = optarg;
145                         break;
146                 case 'o' :
147                         opts |= OPT_SAVEOUT;
148                         break;
149                 case 'r' :
150                         if (loadrules(optarg) == -1)
151                                 return -1;
152                         loaded = 1;
153                         break;
154                 case 's' :
155                         src.s_addr = inet_addr(optarg);
156                         break;
157                 case 'v' :
158                         opts |= OPT_VERBOSE;
159                         break;
160                 case 'E' :
161                         r = &etherf;
162                         break;
163                 case 'H' :
164                         r = &iphex;
165                         break;
166                 case 'N' :
167                         opts |= OPT_NAT;
168                         break;
169                 case 'P' :
170                         r = &pcap;
171                         break;
172                 case 'R' :
173                         rremove = 1;
174                         break;
175                 case 'S' :
176                         r = &snoop;
177                         break;
178                 case 'T' :
179                         r = &tcpd;
180                         break;
181                 case 'x' :
182                         opts |= OPT_HEX;
183                         break;
184                 case 'X' :
185                         r = &iptext;
186                         break;
187                 }
188
189         if (loaded == 0) {
190                 (void)fprintf(stderr,"no rules loaded\n");
191                 exit(-1);
192         }
193
194         if (opts & OPT_SAVEOUT)
195                 init_ifp();
196
197         if (datain)
198                 fd = (*r->r_open)(datain);
199         else
200                 fd = (*r->r_open)("-");
201
202         if (fd < 0)
203                 exit(-1);
204
205         ip = (ip_t *)buf;
206         while ((i = (*r->r_readip)((char *)buf, sizeof(buf),
207                                     &iface, &dir)) > 0) {
208                 if (iface == NULL || *iface == '\0')
209                         iface = ifname;
210                 ifp = get_unit(iface, ip->ip_v);
211                 hlen = 0;
212                 if (!use_inet6) {
213                         ip->ip_off = ntohs(ip->ip_off);
214                         ip->ip_len = ntohs(ip->ip_len);
215                         hlen = ip->ip_hl << 2;
216                         if (src.s_addr != 0) {
217                                 if (src.s_addr == ip->ip_src.s_addr)
218                                         dir = 1;
219                                 else if (src.s_addr == ip->ip_dst.s_addr)
220                                         dir = 0;
221                         }
222                 }
223 #ifdef  USE_INET6
224                 else
225                         hlen = sizeof(ip6_t);
226 #endif
227                 if (opts & OPT_VERBOSE) {
228                         printf("%s on [%s]: ", dir ? "out" : "in",
229                                 (iface && *iface) ? iface : "??");
230                 }
231                 packet = (char *)buf;
232                 /* ipfr_slowtimer(); */
233                 i = fr_check(ip, hlen, ifp, dir, (mb_t **)&packet);
234                 if ((opts & OPT_NAT) == 0)
235                         switch (i)
236                         {
237                         case -5 :
238                                 (void)printf("block return-icmp-as-dest");
239                                 break;
240                         case -4 :
241                                 (void)printf("block return-icmp");
242                                 break;
243                         case -3 :
244                                 (void)printf("block return-rst");
245                                 break;
246                         case -2 :
247                                 (void)printf("auth");
248                                 break;
249                         case -1 :
250                                 (void)printf("block");
251                                 break;
252                         case 0 :
253                                 (void)printf("pass");
254                                 break;
255                         case 1 :
256                                 (void)printf("nomatch");
257                                 break;
258                         }
259                 if (!use_inet6) {
260                         ip->ip_off = htons(ip->ip_off);
261                         ip->ip_len = htons(ip->ip_len);
262                 }
263
264                 if (!(opts & OPT_BRIEF)) {
265                         putchar(' ');
266                         printpacket((ip_t *)buf);
267                         printf("--------------");
268                 } else if ((opts & (OPT_BRIEF|OPT_NAT)) == (OPT_NAT|OPT_BRIEF))
269                         printpacket((ip_t *)buf);
270 #ifndef linux
271                 if (dir && (ifp != NULL) && ip->ip_v && (packet != NULL))
272 # if defined(__sgi) && (IRIX < 605)
273                         (*ifp->if_output)(ifp, (void *)packet, NULL);
274 # else
275                         (*ifp->if_output)(ifp, (void *)packet, NULL, 0);
276 # endif
277 #endif
278                 if ((opts & (OPT_BRIEF|OPT_NAT)) != (OPT_NAT|OPT_BRIEF))
279                         putchar('\n');
280                 dir = 0;
281                 if (iface != ifname) {
282                         free(iface);
283                         iface = ifname;
284                 }
285         }
286         (*r->r_close)();
287
288         if (logout != NULL) {
289                 drain_log(logout);
290         }
291
292         if (dump == 1)  {
293                 dumpnat();
294                 dumpstate();
295         }
296
297         return 0;
298 }
299
300
301 /*
302  * Load in either NAT or ipf rules from a file, which is treated as stdin
303  * if the name is "-".  NOTE, stdin can only be used once as the file is
304  * closed after use.
305  */
306 int loadrules(file)
307 char *file;
308 {
309         char    line[513], *s;
310         int     linenum, i;
311         void    *fr;
312         FILE    *fp;
313         int     parsestatus;
314
315         if (!strcmp(file, "-"))
316                 fp = stdin;
317         else if (!(fp = fopen(file, "r"))) {
318                 (void)fprintf(stderr, "couldn't open %s\n", file);
319                 return (-1);
320         }
321
322         if (!(opts & OPT_BRIEF))
323                 (void)printf("opening rule file \"%s\"\n", file);
324
325         linenum = 0;
326
327         while (fgets(line, sizeof(line) - 1, fp)) {
328                 linenum++;
329
330                 /*
331                  * treat both CR and LF as EOL
332                  */
333                 if ((s = index(line, '\n')))
334                         *s = '\0';
335                 if ((s = index(line, '\r')))
336                         *s = '\0';
337
338                 /*
339                  * # is comment marker, everything after is a ignored
340                  */
341                 if ((s = index(line, '#')))
342                         *s = '\0';
343
344                 if (!*line)
345                         continue;
346
347                 /* fake an `ioctl' call :) */
348
349                 if ((opts & OPT_NAT) != 0) {
350                         parsestatus = 1;
351                         fr = natparse(line, linenum, &parsestatus);
352                         if (parsestatus != 0) {
353                                 if (*line) {
354                                         fprintf(stderr,
355                                             "%d: syntax error in \"%s\"\n",
356                                             linenum, line);
357                                 }
358                                 fprintf(stderr, "%s: %s error (%d), quitting\n",
359                                     file,
360                                     ((parsestatus < 0)? "parse": "internal"),
361                                     parsestatus);
362                                 exit(1);
363                         }
364                         if (!fr)
365                                 continue;
366
367                         if (rremove == 0) {
368                                 i = IPL_EXTERN(ioctl)(IPL_LOGNAT, SIOCADNAT,
369                                                       (caddr_t)&fr,
370                                                       FWRITE|FREAD);
371                                 if (opts & OPT_DEBUG)
372                                         fprintf(stderr,
373                                                 "iplioctl(ADNAT,%p,1) = %d\n",
374                                                 fr, i);
375                         } else {
376                                 i = IPL_EXTERN(ioctl)(IPL_LOGNAT, SIOCRMNAT,
377                                                       (caddr_t)&fr,
378                                                       FWRITE|FREAD);
379                                 if (opts & OPT_DEBUG)
380                                         fprintf(stderr,
381                                                 "iplioctl(RMNAT,%p,1) = %d\n",
382                                                 fr, i);
383                         }
384                 } else {
385                         fr = parse(line, linenum, &parsestatus);
386
387                         if (parsestatus != 0) {
388                             fprintf(stderr, "%s: %s error (%d), quitting\n",
389                                 file,
390                                 ((parsestatus < 0)? "parse": "internal"),
391                                 parsestatus);
392                             exit(1);
393                         }
394
395                         if (!fr) {
396                                 continue;
397                         }
398
399                         if (rremove == 0) {
400                                 i = IPL_EXTERN(ioctl)(0, SIOCADAFR,
401                                                       (caddr_t)&fr,
402                                                       FWRITE|FREAD);
403                                 if (opts & OPT_DEBUG)
404                                         fprintf(stderr,
405                                                 "iplioctl(ADAFR,%p,1) = %d\n",
406                                                 fr, i);
407                         } else {
408                                 i = IPL_EXTERN(ioctl)(0, SIOCRMAFR,
409                                                       (caddr_t)&fr,
410                                                       FWRITE|FREAD);
411                                 if (opts & OPT_DEBUG)
412                                         fprintf(stderr,
413                                                 "iplioctl(RMAFR,%p,1) = %d\n",
414                                                 fr, i);
415                         }
416                 }
417         }
418         (void)fclose(fp);
419
420         return 0;
421 }
422
423
424 int kmemcpy(addr, offset, size)
425 char *addr;
426 long offset;
427 int size;
428 {
429         bcopy((char *)offset, addr, size);
430         return 0;
431 }
432
433
434 /*
435  * Display the built up NAT table rules and mapping entries.
436  */
437 void dumpnat()
438 {
439         ipnat_t *ipn;
440         nat_t   *nat;
441
442         printf("List of active MAP/Redirect filters:\n");
443         for (ipn = nat_list; ipn != NULL; ipn = ipn->in_next)
444                 printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE));
445         printf("\nList of active sessions:\n");
446         for (nat = nat_instances; nat; nat = nat->nat_next)
447                 printactivenat(nat, opts);
448 }
449
450
451 /*
452  * Display the built up state table rules and mapping entries.
453  */
454 void dumpstate()
455 {
456         ipstate_t *ips;
457
458         printf("List of active state sessions:\n");
459         for (ips = ips_list; ips != NULL; )
460                 ips = printstate(ips, opts & (OPT_DEBUG|OPT_VERBOSE));
461 }
462
463
464 /*
465  * Given a pointer to an interface in the kernel, return a pointer to a
466  * string which is the interface name.
467  */
468 char *getifname(ptr)
469 void *ptr;
470 {
471 #if defined(NetBSD) && (NetBSD >= 199905) && (NetBSD < 1991011) || \
472     defined(__OpenBSD__)
473 #else
474         char buf[32], *s;
475         int len;
476 #endif
477         struct ifnet netif;
478
479         if (ptr == (void *)-1)
480                 return "!";
481         if (ptr == NULL)
482                 return "-";
483
484         if (kmemcpy((char *)&netif, (u_long)ptr, sizeof(netif)) == -1)
485                 return "X";
486 #if defined(NetBSD) && (NetBSD >= 199905) && (NetBSD < 1991011) || \
487     defined(__OpenBSD__) || defined(__DragonFly__)
488         return strdup(netif.if_xname);
489 #else
490         if (kmemcpy(buf, (u_long)netif.if_name, sizeof(buf)) == -1)
491                 return "X";
492         if (netif.if_unit < 10)
493                 len = 2;
494         else if (netif.if_unit < 1000)
495                 len = 3;
496         else if (netif.if_unit < 10000)
497                 len = 4;
498         else
499                 len = 5;
500         buf[sizeof(buf) - len] = '\0';
501         for (s = buf; *s && !isdigit(*s); s++)
502                 ;
503         if (isdigit(*s))
504                 *s = '\0';
505         sprintf(buf + strlen(buf), "%d", netif.if_unit % 10000);
506         return strdup(buf);
507 #endif
508 }
509
510
511 void drain_log(filename)
512 char *filename;
513 {
514         char buffer[IPLLOGSIZE];
515         struct iovec iov;
516         struct uio uio;
517         size_t resid;
518         int fd;
519
520         fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644);
521         if (fd == -1) {
522                 perror("drain_log:open");
523                 return;
524         }
525
526         while (1) {
527                 bzero((char *)&iov, sizeof(iov));
528                 iov.iov_base = buffer;
529                 iov.iov_len = sizeof(buffer);
530
531                 bzero((char *)&uio, sizeof(uio));
532                 uio.uio_iov = &iov;
533                 uio.uio_iovcnt = 1;
534                 uio.uio_resid = iov.iov_len;
535                 resid = uio.uio_resid;
536
537                 if (ipflog_read(0, &uio) == 0) {
538                         /*
539                          * If nothing was read then break out.
540                          */
541                         if (uio.uio_resid == resid)
542                                 break;
543                         write(fd, buffer, resid - uio.uio_resid);
544                 } else
545                         break;
546         }
547
548         close(fd);
549 }