filter.c revision 31962
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.20 1997/12/13 02:37:22 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 const *const *argv,
66	  struct in_addr * paddr,
67	  struct in_addr * pmask,
68	  int *pwidth)
69{
70  int bits, len;
71  char *wp;
72  const char *cp;
73
74  if (argc < 1) {
75    LogPrintf(LogWARN, "ParseAddr: address/mask is expected.\n");
76    return (0);
77  }
78
79  if (pmask)
80    pmask->s_addr = 0xffffffff;	/* Assume 255.255.255.255 as default */
81
82  cp = pmask || pwidth ? strchr(*argv, '/') : NULL;
83  len = cp ? cp - *argv : strlen(*argv);
84
85  if (strncasecmp(*argv, "HISADDR", len) == 0)
86    *paddr = IpcpInfo.his_ipaddr;
87  else if (strncasecmp(*argv, "MYADDR", len) == 0)
88    *paddr = IpcpInfo.want_ipaddr;
89  else if (len > 15)
90    LogPrintf(LogWARN, "ParseAddr: %s: Bad address\n", *argv);
91  else {
92    char s[16];
93    strncpy(s, *argv, len);
94    s[len] = '\0';
95    if (inet_aton(s, paddr) == 0) {
96      LogPrintf(LogWARN, "ParseAddr: %s: Bad address\n", s);
97      return (0);
98    }
99  }
100  if (cp && *++cp) {
101    bits = strtol(cp, &wp, 0);
102    if (cp == wp || bits < 0 || bits > 32) {
103      LogPrintf(LogWARN, "ParseAddr: bad mask width.\n");
104      return (0);
105    }
106  } else {
107    /* if width is not given, assume whole 32 bits are meaningfull */
108    bits = 32;
109  }
110
111  if (pwidth)
112    *pwidth = bits;
113
114  if (pmask)
115    pmask->s_addr = htonl(netmasks[bits]);
116
117  return (1);
118}
119
120static int
121ParseProto(int argc, char const *const *argv)
122{
123  int proto;
124
125  if (argc < 1)
126    return (P_NONE);
127
128  if (!strcmp(*argv, "tcp"))
129    proto = P_TCP;
130  else if (!strcmp(*argv, "udp"))
131    proto = P_UDP;
132  else if (!strcmp(*argv, "icmp"))
133    proto = P_ICMP;
134  else
135    proto = P_NONE;
136  return (proto);
137}
138
139static int
140ParsePort(const char *service, int proto)
141{
142  const char *protocol_name;
143  char *cp;
144  struct servent *servent;
145  int port;
146
147  switch (proto) {
148  case P_UDP:
149    protocol_name = "udp";
150    break;
151  case P_TCP:
152    protocol_name = "tcp";
153    break;
154  default:
155    protocol_name = 0;
156  }
157
158  servent = getservbyname(service, protocol_name);
159  if (servent != 0)
160    return (ntohs(servent->s_port));
161
162  port = strtol(service, &cp, 0);
163  if (cp == service) {
164    LogPrintf(LogWARN, "ParsePort: %s is not a port name or number.\n",
165	      service);
166    return (0);
167  }
168  return (port);
169}
170
171/*
172 *	ICMP Syntax:	src eq icmp_message_type
173 */
174static int
175ParseIcmp(int argc, char const *const *argv)
176{
177  int type;
178  char *cp;
179
180  switch (argc) {
181  case 0:
182    /* permit/deny all ICMP types */
183    filterdata.opt.srcop = OP_NONE;
184    break;
185  default:
186    LogPrintf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
187    return (0);
188  case 3:
189    if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
190      type = strtol(argv[2], &cp, 0);
191      if (cp == argv[2]) {
192	LogPrintf(LogWARN, "ParseIcmp: type is expected.\n");
193	return (0);
194      }
195      filterdata.opt.srcop = OP_EQ;
196      filterdata.opt.srcport = type;
197    }
198    break;
199  }
200  return (1);
201}
202
203static int
204ParseOp(const char *cp)
205{
206  int op = OP_NONE;
207
208  if (!strcmp(cp, "eq"))
209    op = OP_EQ;
210  else if (!strcmp(cp, "gt"))
211    op = OP_GT;
212  else if (!strcmp(cp, "lt"))
213    op = OP_LT;
214  return (op);
215}
216
217/*
218 *	UDP Syntax: [src op port] [dst op port]
219 */
220static int
221ParseUdpOrTcp(int argc, char const *const *argv, int proto)
222{
223  filterdata.opt.srcop = filterdata.opt.dstop = OP_NONE;
224  filterdata.opt.estab = 0;
225
226  if (argc == 0) {
227    /* permit/deny all tcp traffic */
228    return (1);
229  }
230
231  if (argc >= 3 && !strcmp(*argv, "src")) {
232    filterdata.opt.srcop = ParseOp(argv[1]);
233    if (filterdata.opt.srcop == OP_NONE) {
234      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
235      return (0);
236    }
237    filterdata.opt.srcport = ParsePort(argv[2], proto);
238    if (filterdata.opt.srcport == 0)
239      return (0);
240    argc -= 3;
241    argv += 3;
242    if (argc == 0)
243      return (1);
244  }
245  if (argc >= 3 && !strcmp(argv[0], "dst")) {
246    filterdata.opt.dstop = ParseOp(argv[1]);
247    if (filterdata.opt.dstop == OP_NONE) {
248      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
249      return (0);
250    }
251    filterdata.opt.dstport = ParsePort(argv[2], proto);
252    if (filterdata.opt.dstport == 0)
253      return (0);
254    argc -= 3;
255    argv += 3;
256    if (argc == 0)
257      return (1);
258  }
259  if (argc == 1 && proto == P_TCP) {
260    if (!strcmp(*argv, "estab")) {
261      filterdata.opt.estab = 1;
262      return (1);
263    }
264    LogPrintf(LogWARN, "ParseUdpOrTcp: estab is expected: %s\n", *argv);
265    return (0);
266  }
267  if (argc > 0)
268    LogPrintf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
269  return (0);
270}
271
272const char *opname[] = {"none", "eq", "gt", NULL, "lt"};
273
274static int
275Parse(int argc, char const *const *argv, struct filterent * ofp)
276{
277  int action, proto;
278  int val;
279  char *wp;
280  struct filterent *fp = &filterdata;
281
282  val = strtol(*argv, &wp, 0);
283  if (*argv == wp || val > MAXFILTERS) {
284    LogPrintf(LogWARN, "Parse: invalid filter number.\n");
285    return (0);
286  }
287  if (val < 0) {
288    for (val = 0; val < MAXFILTERS; val++) {
289      ofp->action = A_NONE;
290      ofp++;
291    }
292    LogPrintf(LogWARN, "Parse: filter cleared.\n");
293    return (1);
294  }
295  ofp += val;
296
297  if (--argc == 0) {
298    LogPrintf(LogWARN, "Parse: missing action.\n");
299    return (0);
300  }
301  argv++;
302
303  proto = P_NONE;
304  memset(&filterdata, '\0', sizeof filterdata);
305
306  if (!strcmp(*argv, "permit")) {
307    action = A_PERMIT;
308  } else if (!strcmp(*argv, "deny")) {
309    action = A_DENY;
310  } else if (!strcmp(*argv, "clear")) {
311    ofp->action = A_NONE;
312    return (1);
313  } else {
314    LogPrintf(LogWARN, "Parse: bad action: %s\n", *argv);
315    return (0);
316  }
317  fp->action = action;
318
319  argc--;
320  argv++;
321
322  if (fp->action == A_DENY) {
323    if (!strcmp(*argv, "host")) {
324      fp->action |= A_UHOST;
325      argc--;
326      argv++;
327    } else if (!strcmp(*argv, "port")) {
328      fp->action |= A_UPORT;
329      argc--;
330      argv++;
331    }
332  }
333  proto = ParseProto(argc, argv);
334  if (proto == P_NONE) {
335    if (ParseAddr(argc, argv, &fp->saddr, &fp->smask, &fp->swidth)) {
336      argc--;
337      argv++;
338      proto = ParseProto(argc, argv);
339      if (proto == P_NONE) {
340	if (ParseAddr(argc, argv, &fp->daddr, &fp->dmask, &fp->dwidth)) {
341	  argc--;
342	  argv++;
343	}
344	proto = ParseProto(argc, argv);
345	if (proto != P_NONE) {
346	  argc--;
347	  argv++;
348	}
349      } else {
350	argc--;
351	argv++;
352      }
353    } else {
354      LogPrintf(LogWARN, "Parse: Address/protocol expected.\n");
355      return (0);
356    }
357  } else {
358    argc--;
359    argv++;
360  }
361
362  val = 1;
363  fp->proto = proto;
364
365  switch (proto) {
366  case P_TCP:
367    val = ParseUdpOrTcp(argc, argv, P_TCP);
368    break;
369  case P_UDP:
370    val = ParseUdpOrTcp(argc, argv, P_UDP);
371    break;
372  case P_ICMP:
373    val = ParseIcmp(argc, argv);
374    break;
375  }
376
377  LogPrintf(LogDEBUG, "Parse: Src: %s\n", inet_ntoa(fp->saddr));
378  LogPrintf(LogDEBUG, "Parse: Src mask: %s\n", inet_ntoa(fp->smask));
379  LogPrintf(LogDEBUG, "Parse: Dst: %s\n", inet_ntoa(fp->daddr));
380  LogPrintf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(fp->dmask));
381  LogPrintf(LogDEBUG, "Parse: Proto = %d\n", proto);
382
383  LogPrintf(LogDEBUG, "Parse: src:  %s (%d)\n", opname[fp->opt.srcop],
384	    fp->opt.srcport);
385  LogPrintf(LogDEBUG, "Parse: dst:  %s (%d)\n", opname[fp->opt.dstop],
386	    fp->opt.dstport);
387  LogPrintf(LogDEBUG, "Parse: estab: %d\n", fp->opt.estab);
388
389  if (val)
390    *ofp = *fp;
391  return (val);
392}
393
394int
395SetIfilter(struct cmdargs const *arg)
396{
397  if (arg->argc > 0) {
398    Parse(arg->argc, arg->argv, ifilters);
399    return 0;
400  }
401  return -1;
402}
403
404int
405SetOfilter(struct cmdargs const *arg)
406{
407  if (arg->argc > 0) {
408    (void) Parse(arg->argc, arg->argv, ofilters);
409    return 0;
410  }
411  return -1;
412}
413
414int
415SetDfilter(struct cmdargs const *arg)
416{
417  if (arg->argc > 0) {
418    (void) Parse(arg->argc, arg->argv, dfilters);
419    return 0;
420  }
421  return -1;
422}
423
424int
425SetAfilter(struct cmdargs const *arg)
426{
427  if (arg->argc > 0) {
428    (void) Parse(arg->argc, arg->argv, afilters);
429    return 0;
430  }
431  return -1;
432}
433
434static const char *protoname[] = { "none", "tcp", "udp", "icmp" };
435static const char *actname[] = { "none   ", "permit ", "deny   " };
436
437static void
438ShowFilter(struct filterent * fp)
439{
440  int n;
441
442  if (!VarTerm)
443    return;
444
445  for (n = 0; n < MAXFILTERS; n++, fp++) {
446    if (fp->action != A_NONE) {
447      fprintf(VarTerm, "%2d %s", n, actname[fp->action & (A_PERMIT|A_DENY)]);
448      if (fp->action & A_UHOST)
449        fprintf(VarTerm, "host ");
450      else if (fp->action & A_UPORT)
451        fprintf(VarTerm, "port ");
452      else
453        fprintf(VarTerm, "     ");
454      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
455      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
456      if (fp->proto) {
457	fprintf(VarTerm, "%s", protoname[fp->proto]);
458
459	if (fp->opt.srcop)
460	  fprintf(VarTerm, " src %s %d", opname[fp->opt.srcop],
461		  fp->opt.srcport);
462	if (fp->opt.dstop)
463	  fprintf(VarTerm, " dst %s %d", opname[fp->opt.dstop],
464		  fp->opt.dstport);
465	if (fp->opt.estab)
466	  fprintf(VarTerm, " estab");
467
468      }
469      fprintf(VarTerm, "\n");
470    }
471  }
472}
473
474int
475ShowIfilter(struct cmdargs const *arg)
476{
477  ShowFilter(ifilters);
478  return 0;
479}
480
481int
482ShowOfilter(struct cmdargs const *arg)
483{
484  ShowFilter(ofilters);
485  return 0;
486}
487
488int
489ShowDfilter(struct cmdargs const *arg)
490{
491  ShowFilter(dfilters);
492  return 0;
493}
494
495int
496ShowAfilter(struct cmdargs const *arg)
497{
498  ShowFilter(afilters);
499  return 0;
500}
501