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