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