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