filter.c revision 22997
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$
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
204  if (argc == 0) {
205    /* permit/deny all tcp traffic */
206    filterdata.opt.srcop = filterdata.opt.dstop = A_NONE;
207    return(1);
208  }
209  if (argc < 3) {
210#ifdef notdef
211    printf("bad udp syntax.\n");
212#endif
213    return(0);
214  }
215  if (argc >= 3 && STREQ(*argv, "src")) {
216    filterdata.opt.srcop = ParseOp(argv[1]);
217    if (filterdata.opt.srcop == OP_NONE) {
218      printf("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; argv += 3;
225    if (argc == 0)
226      return(1);
227  }
228  if (argc >= 3 && STREQ(argv[0], "dst")) {
229    filterdata.opt.dstop = ParseOp(argv[1]);
230    if (filterdata.opt.dstop == OP_NONE) {
231      printf("bad operation\n");
232      return(0);
233    }
234    filterdata.opt.dstport = ParsePort(argv[2], proto);
235    if (filterdata.opt.dstport == 0)
236      return(0);
237    argc -= 3; argv += 3;
238    if (argc == 0)
239      return(1);
240  }
241  if (argc == 1) {
242    if (STREQ(*argv, "estab")) {
243      filterdata.opt.estab = 1;
244      return(1);
245    }
246    printf("estab is expected: %s\n", *argv);
247    return(0);
248  }
249  if (argc > 0)
250    printf("bad src/dst port syntax: %s\n", *argv);
251  return(0);
252}
253
254char *opname[] = { "none", "eq", "gt", "lt" };
255
256static int
257Parse(argc, argv, ofp)
258int argc;
259char **argv;
260struct 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    printf("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    printf("filter cleard.\n");
278    return(1);
279  }
280  ofp += val;
281
282  if (--argc == 0) {
283    printf("missing action.\n");
284    return(0);
285  }
286  argv++;
287
288  proto = P_NONE;
289  bzero(&filterdata, sizeof(filterdata));
290
291  if (STREQ(*argv, "permit")) {
292    action = A_PERMIT;
293  } else if (STREQ(*argv, "deny")) {
294    action = A_DENY;
295  } else if (STREQ(*argv, "clear")) {
296    ofp->action = A_NONE;
297    return(1);
298  } else {
299    printf("bad action: %s\n", *argv);
300    return(0);
301  }
302  fp->action = action;
303
304  argc--; argv++;
305
306  if (ofp->action == A_DENY) {
307    if (STREQ(*argv, "host")) {
308      fp->action |= A_UHOST;
309      argc--; argv++;
310    } else if (STREQ(*argv, "port")) {
311      fp->action |= A_UPORT;
312      argc--; argv++;
313    }
314  }
315
316  proto = ParseProto(argc, argv);
317  if (proto == P_NONE) {
318    if (ParseAddr(argc, argv, &fp->saddr, &fp->smask, &fp->swidth)) {
319      argc--; argv++;
320      proto = ParseProto(argc, argv);
321      if (proto == P_NONE) {
322	if (ParseAddr(argc, argv, &fp->daddr, &fp->dmask, &fp->dwidth)) {
323	  argc--; argv++;
324	}
325	proto = ParseProto(argc, argv);
326	if (proto) {
327	  argc--; argv++;
328	}
329      }
330    } else {
331      printf("Address/protocol expected.\n");
332      return(0);
333    }
334  } else {
335    argc--; argv++;
336  }
337
338  val = 1;
339  fp->proto = proto;
340
341  switch (proto) {
342  case P_TCP:
343    val = ParseUdpOrTcp(argc, argv, P_TCP);
344    break;
345  case P_UDP:
346    val = ParseUdpOrTcp(argc, argv, P_UDP);
347    break;
348  case P_ICMP:
349    val = ParseIcmp(argc, argv);
350    break;
351  }
352
353#ifdef DEBUG
354  printf("src: %s/", inet_ntoa(fp->saddr));
355  printf("%s ", inet_ntoa(fp->smask));
356  printf("dst: %s/", inet_ntoa(fp->daddr));
357  printf("%s proto = %d\n", inet_ntoa(fp->dmask), proto);
358
359  printf("src:  %s (%d)\n", opname[fp->opt.srcop], fp->opt.srcport);
360  printf("dst:  %s (%d)\n", opname[fp->opt.dstop], fp->opt.dstport);
361  printf("estab: %d\n", fp->opt.estab);
362#endif
363
364  if (val)
365    *ofp = *fp;
366  return(val);
367}
368
369int
370SetIfilter(list, argc, argv)
371struct cmdtab *list;
372int argc;
373char **argv;
374{
375  if (argc > 0)
376    (void) Parse(argc, argv, ifilters);
377  else
378    printf("syntax error.\n");
379
380  return(1);
381}
382
383int
384SetOfilter(list, argc, argv)
385struct cmdtab *list;
386int argc;
387char **argv;
388{
389  if (argc > 0)
390    (void) Parse(argc, argv, ofilters);
391  else
392    printf("syntax error.\n");
393  return(1);
394}
395
396int
397SetDfilter(list, argc, argv)
398struct cmdtab *list;
399int argc;
400char **argv;
401{
402  if (argc > 0)
403    (void) Parse(argc, argv, dfilters);
404  else
405    printf("syntax error.\n");
406  return(1);
407}
408
409int
410SetAfilter(list, argc, argv)
411struct cmdtab *list;
412int argc;
413char **argv;
414{
415  if (argc > 0)
416    (void) Parse(argc, argv, afilters);
417  else
418    printf("syntax error.\n");
419  return(1);
420}
421
422static char *protoname[] = {
423  "none", "tcp", "udp", "icmp",
424};
425
426static char *actname[] = {
427  "none   ", "permit ", "deny   ",
428};
429
430static void
431ShowFilter(fp)
432struct filterent *fp;
433{
434  int n;
435
436  for (n = 0; n < MAXFILTERS; n++, fp++) {
437    if (fp->action != A_NONE) {
438      printf("%2d %s", n, actname[fp->action]);
439
440      printf("%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
441      printf("%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
442      if (fp->proto) {
443	printf("%s", protoname[fp->proto]);
444
445	if (fp->opt.srcop)
446	  printf(" src %s %d", opname[fp->opt.srcop], fp->opt.srcport);
447	if (fp->opt.dstop)
448	  printf(" dst %s %d", opname[fp->opt.dstop], fp->opt.dstport);
449	if (fp->opt.estab)
450	  printf(" estab");
451
452      }
453      printf("\n");
454    }
455  }
456}
457
458int
459ShowIfilter(list, argc, argv)
460struct cmdtab *list;
461int argc;
462char **argv;
463{
464  ShowFilter(ifilters);
465  return(1);
466}
467
468int
469ShowOfilter(list, argc, argv)
470struct cmdtab *list;
471int argc;
472char **argv;
473{
474  ShowFilter(ofilters);
475  return(1);
476}
477
478int
479ShowDfilter(list, argc, argv)
480struct cmdtab *list;
481int argc;
482char **argv;
483{
484  ShowFilter(dfilters);
485  return(1);
486}
487
488int
489ShowAfilter(list, argc, argv)
490struct cmdtab *list;
491int argc;
492char **argv;
493{
494  ShowFilter(afilters);
495  return(1);
496}
497