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