filter.c revision 31070
1/*
2 *		PPP Filter command Interface
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: filter.c,v 1.16 1997/10/26 01:02:34 brian Exp $
21 *
22 *	TODO: Shoud send ICMP error message when we discard packets.
23 */
24
25#include <sys/param.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29#include <netdb.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <strings.h>
34
35#include "command.h"
36#include "mbuf.h"
37#include "log.h"
38#include "loadalias.h"
39#include "defs.h"
40#include "vars.h"
41#include "ipcp.h"
42#include "filter.h"
43
44struct filterent ifilters[MAXFILTERS];	/* incoming packet filter */
45struct filterent ofilters[MAXFILTERS];	/* outgoing packet filter */
46struct filterent dfilters[MAXFILTERS];	/* dial-out packet filter */
47struct filterent afilters[MAXFILTERS];	/* keep-alive packet filter */
48
49static struct filterent filterdata;
50
51static u_long netmasks[33] = {
52  0x00000000,
53  0x80000000, 0xC0000000, 0xE0000000, 0xF0000000,
54  0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000,
55  0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000,
56  0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
57  0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
58  0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00,
59  0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0,
60  0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF,
61};
62
63int
64ParseAddr(int argc,
65	  char **argv,
66	  struct in_addr * paddr,
67	  struct in_addr * pmask,
68	  int *pwidth)
69{
70  int bits;
71  char *cp, *wp;
72
73  if (argc < 1) {
74    LogPrintf(LogWARN, "ParseAddr: address/mask is expected.\n");
75    return (0);
76  }
77  pmask->s_addr = 0xffffffff;	/* Assume 255.255.255.255 as default */
78  cp = strchr(*argv, '/');
79  if (cp)
80    *cp++ = '\0';
81  if (strcasecmp(*argv, "HISADDR") == 0)
82    *paddr = IpcpInfo.his_ipaddr;
83  else if (strcasecmp(*argv, "MYADDR") == 0)
84    *paddr = IpcpInfo.want_ipaddr;
85  else if (inet_aton(*argv, paddr) == 0) {
86    LogPrintf(LogWARN, "ParseAddr: %s: Bad address\n", *argv);
87    return (0);
88  }
89  if (cp && *cp) {
90    bits = strtol(cp, &wp, 0);
91    if (cp == wp || bits < 0 || bits > 32) {
92      LogPrintf(LogWARN, "ParseAddr: bad mask width.\n");
93      return (0);
94    }
95  } else {
96    /* if width is not given, assume whole 32 bits are meaningfull */
97    bits = 32;
98  }
99
100  *pwidth = bits;
101  pmask->s_addr = htonl(netmasks[bits]);
102
103  return (1);
104}
105
106static int
107ParseProto(int argc, char **argv)
108{
109  int proto;
110
111  if (argc < 1)
112    return (P_NONE);
113
114  if (!strcmp(*argv, "tcp"))
115    proto = P_TCP;
116  else if (!strcmp(*argv, "udp"))
117    proto = P_UDP;
118  else if (!strcmp(*argv, "icmp"))
119    proto = P_ICMP;
120  else
121    proto = P_NONE;
122  return (proto);
123}
124
125static int
126ParsePort(char *service, int proto)
127{
128  char *protocol_name, *cp;
129  struct servent *servent;
130  int port;
131
132  switch (proto) {
133  case P_UDP:
134    protocol_name = "udp";
135    break;
136  case P_TCP:
137    protocol_name = "tcp";
138    break;
139  default:
140    protocol_name = 0;
141  }
142
143  servent = getservbyname(service, protocol_name);
144  if (servent != 0)
145    return (ntohs(servent->s_port));
146
147  port = strtol(service, &cp, 0);
148  if (cp == service) {
149    LogPrintf(LogWARN, "ParsePort: %s is not a port name or number.\n",
150	      service);
151    return (0);
152  }
153  return (port);
154}
155
156/*
157 *	ICMP Syntax:	src eq icmp_message_type
158 */
159static int
160ParseIcmp(int argc, char **argv)
161{
162  int type;
163  char *cp;
164
165  switch (argc) {
166  case 0:
167    /* permit/deny all ICMP types */
168    filterdata.opt.srcop = OP_NONE;
169    break;
170  default:
171    LogPrintf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
172    return (0);
173  case 3:
174    if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
175      type = strtol(argv[2], &cp, 0);
176      if (cp == argv[2]) {
177	LogPrintf(LogWARN, "ParseIcmp: type is expected.\n");
178	return (0);
179      }
180      filterdata.opt.srcop = OP_EQ;
181      filterdata.opt.srcport = type;
182    }
183    break;
184  }
185  return (1);
186}
187
188static int
189ParseOp(char *cp)
190{
191  int op = OP_NONE;
192
193  if (!strcmp(cp, "eq"))
194    op = OP_EQ;
195  else if (!strcmp(cp, "gt"))
196    op = OP_GT;
197  else if (!strcmp(cp, "lt"))
198    op = OP_LT;
199  return (op);
200}
201
202/*
203 *	UDP Syntax: [src op port] [dst op port]
204 */
205static int
206ParseUdpOrTcp(int argc, char **argv, int proto)
207{
208  filterdata.opt.srcop = filterdata.opt.dstop = OP_NONE;
209  filterdata.opt.estab = 0;
210
211  if (argc == 0) {
212    /* permit/deny all tcp traffic */
213    return (1);
214  }
215
216  if (argc >= 3 && !strcmp(*argv, "src")) {
217    filterdata.opt.srcop = ParseOp(argv[1]);
218    if (filterdata.opt.srcop == OP_NONE) {
219      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
220      return (0);
221    }
222    filterdata.opt.srcport = ParsePort(argv[2], proto);
223    if (filterdata.opt.srcport == 0)
224      return (0);
225    argc -= 3;
226    argv += 3;
227    if (argc == 0)
228      return (1);
229  }
230  if (argc >= 3 && !strcmp(argv[0], "dst")) {
231    filterdata.opt.dstop = ParseOp(argv[1]);
232    if (filterdata.opt.dstop == OP_NONE) {
233      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
234      return (0);
235    }
236    filterdata.opt.dstport = ParsePort(argv[2], proto);
237    if (filterdata.opt.dstport == 0)
238      return (0);
239    argc -= 3;
240    argv += 3;
241    if (argc == 0)
242      return (1);
243  }
244  if (argc == 1 && proto == P_TCP) {
245    if (!strcmp(*argv, "estab")) {
246      filterdata.opt.estab = 1;
247      return (1);
248    }
249    LogPrintf(LogWARN, "ParseUdpOrTcp: estab is expected: %s\n", *argv);
250    return (0);
251  }
252  if (argc > 0)
253    LogPrintf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
254  return (0);
255}
256
257char *opname[] = {"none", "eq", "gt", NULL, "lt"};
258
259static int
260Parse(int argc, char **argv, struct filterent * ofp)
261{
262  int action, proto;
263  int val;
264  char *wp;
265  struct filterent *fp = &filterdata;
266
267  val = strtol(*argv, &wp, 0);
268  if (*argv == wp || val > MAXFILTERS) {
269    LogPrintf(LogWARN, "Parse: invalid filter number.\n");
270    return (0);
271  }
272  if (val < 0) {
273    for (val = 0; val < MAXFILTERS; val++) {
274      ofp->action = A_NONE;
275      ofp++;
276    }
277    LogPrintf(LogWARN, "Parse: filter cleared.\n");
278    return (1);
279  }
280  ofp += val;
281
282  if (--argc == 0) {
283    LogPrintf(LogWARN, "Parse: missing action.\n");
284    return (0);
285  }
286  argv++;
287
288  proto = P_NONE;
289  memset(&filterdata, '\0', sizeof(filterdata));
290
291  if (!strcmp(*argv, "permit")) {
292    action = A_PERMIT;
293  } else if (!strcmp(*argv, "deny")) {
294    action = A_DENY;
295  } else if (!strcmp(*argv, "clear")) {
296    ofp->action = A_NONE;
297    return (1);
298  } else {
299    LogPrintf(LogWARN, "Parse: bad action: %s\n", *argv);
300    return (0);
301  }
302  fp->action = action;
303
304  argc--;
305  argv++;
306
307  if (fp->action == A_DENY) {
308    if (!strcmp(*argv, "host")) {
309      fp->action |= A_UHOST;
310      argc--;
311      argv++;
312    } else if (!strcmp(*argv, "port")) {
313      fp->action |= A_UPORT;
314      argc--;
315      argv++;
316    }
317  }
318  proto = ParseProto(argc, argv);
319  if (proto == P_NONE) {
320    if (ParseAddr(argc, argv, &fp->saddr, &fp->smask, &fp->swidth)) {
321      argc--;
322      argv++;
323      proto = ParseProto(argc, argv);
324      if (proto == P_NONE) {
325	if (ParseAddr(argc, argv, &fp->daddr, &fp->dmask, &fp->dwidth)) {
326	  argc--;
327	  argv++;
328	}
329	proto = ParseProto(argc, argv);
330	if (proto != P_NONE) {
331	  argc--;
332	  argv++;
333	}
334      } else {
335	argc--;
336	argv++;
337      }
338    } else {
339      LogPrintf(LogWARN, "Parse: Address/protocol expected.\n");
340      return (0);
341    }
342  } else {
343    argc--;
344    argv++;
345  }
346
347  val = 1;
348  fp->proto = proto;
349
350  switch (proto) {
351  case P_TCP:
352    val = ParseUdpOrTcp(argc, argv, P_TCP);
353    break;
354  case P_UDP:
355    val = ParseUdpOrTcp(argc, argv, P_UDP);
356    break;
357  case P_ICMP:
358    val = ParseIcmp(argc, argv);
359    break;
360  }
361
362  LogPrintf(LogDEBUG, "Parse: Src: %s\n", inet_ntoa(fp->saddr));
363  LogPrintf(LogDEBUG, "Parse: Src mask: %s\n", inet_ntoa(fp->smask));
364  LogPrintf(LogDEBUG, "Parse: Dst: %s\n", inet_ntoa(fp->daddr));
365  LogPrintf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(fp->dmask));
366  LogPrintf(LogDEBUG, "Parse: Proto = %d\n", proto);
367
368  LogPrintf(LogDEBUG, "Parse: src:  %s (%d)\n", opname[fp->opt.srcop],
369	    fp->opt.srcport);
370  LogPrintf(LogDEBUG, "Parse: dst:  %s (%d)\n", opname[fp->opt.dstop],
371	    fp->opt.dstport);
372  LogPrintf(LogDEBUG, "Parse: estab: %d\n", fp->opt.estab);
373
374  if (val)
375    *ofp = *fp;
376  return (val);
377}
378
379int
380SetIfilter(struct cmdtab *list, int argc, char **argv)
381{
382  if (argc > 0) {
383    (void) Parse(argc, argv, ifilters);
384    return 0;
385  }
386  return -1;
387}
388
389int
390SetOfilter(struct cmdtab *list, int argc, char **argv)
391{
392  if (argc > 0) {
393    (void) Parse(argc, argv, ofilters);
394    return 0;
395  }
396  return -1;
397}
398
399int
400SetDfilter(struct cmdtab *list, int argc, char **argv)
401{
402  if (argc > 0) {
403    (void) Parse(argc, argv, dfilters);
404    return 0;
405  }
406  return -1;
407}
408
409int
410SetAfilter(struct cmdtab *list, int argc, char **argv)
411{
412  if (argc > 0) {
413    (void) Parse(argc, argv, afilters);
414    return 0;
415  }
416  return -1;
417}
418
419static char *protoname[] = {
420  "none", "tcp", "udp", "icmp",
421};
422
423static char *actname[] = {
424  "none   ", "permit ", "deny   ",
425};
426
427static void
428ShowFilter(struct filterent * fp)
429{
430  int n;
431
432  if (!VarTerm)
433    return;
434
435  for (n = 0; n < MAXFILTERS; n++, fp++) {
436    if (fp->action != A_NONE) {
437      fprintf(VarTerm, "%2d %s", n, actname[fp->action]);
438      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
439      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
440      if (fp->proto) {
441	fprintf(VarTerm, "%s", protoname[fp->proto]);
442
443	if (fp->opt.srcop)
444	  fprintf(VarTerm, " src %s %d", opname[fp->opt.srcop],
445		  fp->opt.srcport);
446	if (fp->opt.dstop)
447	  fprintf(VarTerm, " dst %s %d", opname[fp->opt.dstop],
448		  fp->opt.dstport);
449	if (fp->opt.estab)
450	  fprintf(VarTerm, " estab");
451
452      }
453      fprintf(VarTerm, "\n");
454    }
455  }
456}
457
458int
459ShowIfilter(struct cmdtab * list, int argc, char **argv)
460{
461  ShowFilter(ifilters);
462  return 0;
463}
464
465int
466ShowOfilter(struct cmdtab * list, int argc, char **argv)
467{
468  ShowFilter(ofilters);
469  return 0;
470}
471
472int
473ShowDfilter(struct cmdtab * list, int argc, char **argv)
474{
475  ShowFilter(dfilters);
476  return 0;
477}
478
479int
480ShowAfilter(struct cmdtab * list, int argc, char **argv)
481{
482  ShowFilter(afilters);
483  return 0;
484}
485