filter.c revision 40561
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.25 1998/06/27 12:03:48 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 (ipcp && strncasecmp(*argv, "HISADDR", len) == 0)
94    *paddr = ipcp->peer_ip;
95  else if (ipcp && 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    if (paddr->s_addr == INADDR_ANY)
126      pmask->s_addr = INADDR_ANY;
127    else
128      pmask->s_addr = htonl(netmasks[bits]);
129  }
130
131  return (1);
132}
133
134static int
135ParsePort(const char *service, int proto)
136{
137  const char *protocol_name;
138  char *cp;
139  struct servent *servent;
140  int port;
141
142  switch (proto) {
143  case P_UDP:
144    protocol_name = "udp";
145    break;
146  case P_TCP:
147    protocol_name = "tcp";
148    break;
149  default:
150    protocol_name = 0;
151  }
152
153  servent = getservbyname(service, protocol_name);
154  if (servent != 0)
155    return (ntohs(servent->s_port));
156
157  port = strtol(service, &cp, 0);
158  if (cp == service) {
159    log_Printf(LogWARN, "ParsePort: %s is not a port name or number.\n",
160	      service);
161    return (0);
162  }
163  return (port);
164}
165
166/*
167 *	ICMP Syntax:	src eq icmp_message_type
168 */
169static int
170ParseIcmp(int argc, char const *const *argv, struct filterent *tgt)
171{
172  int type;
173  char *cp;
174
175  switch (argc) {
176  case 0:
177    /* permit/deny all ICMP types */
178    tgt->opt.srcop = OP_NONE;
179    break;
180
181  case 3:
182    if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
183      type = strtol(argv[2], &cp, 0);
184      if (cp == argv[2]) {
185	log_Printf(LogWARN, "ParseIcmp: type is expected.\n");
186	return (0);
187      }
188      tgt->opt.srcop = OP_EQ;
189      tgt->opt.srcport = type;
190    }
191    break;
192
193  default:
194    log_Printf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
195    return (0);
196  }
197  return (1);
198}
199
200/*
201 *	UDP Syntax: [src op port] [dst op port]
202 */
203static int
204ParseUdpOrTcp(int argc, char const *const *argv, int proto,
205              struct filterent *tgt)
206{
207  tgt->opt.srcop = tgt->opt.dstop = OP_NONE;
208  tgt->opt.estab = tgt->opt.syn = tgt->opt.finrst = 0;
209
210  if (argc >= 3 && !strcmp(*argv, "src")) {
211    tgt->opt.srcop = filter_Nam2Op(argv[1]);
212    if (tgt->opt.srcop == OP_NONE) {
213      log_Printf(LogWARN, "ParseUdpOrTcp: bad operation\n");
214      return (0);
215    }
216    tgt->opt.srcport = ParsePort(argv[2], proto);
217    if (tgt->opt.srcport == 0)
218      return (0);
219    argc -= 3;
220    argv += 3;
221  }
222
223  if (argc >= 3 && !strcmp(argv[0], "dst")) {
224    tgt->opt.dstop = filter_Nam2Op(argv[1]);
225    if (tgt->opt.dstop == OP_NONE) {
226      log_Printf(LogWARN, "ParseUdpOrTcp: bad operation\n");
227      return (0);
228    }
229    tgt->opt.dstport = ParsePort(argv[2], proto);
230    if (tgt->opt.dstport == 0)
231      return (0);
232    argc -= 3;
233    argv += 3;
234  }
235
236  if (proto == P_TCP) {
237    for (; argc > 0; argc--, argv++)
238      if (!strcmp(*argv, "estab"))
239        tgt->opt.estab = 1;
240      else if (!strcmp(*argv, "syn"))
241        tgt->opt.syn = 1;
242      else if (!strcmp(*argv, "finrst"))
243        tgt->opt.finrst = 1;
244      else
245        break;
246  }
247
248  if (argc > 0) {
249    log_Printf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
250    return 0;
251  }
252
253  return 1;
254}
255
256static int
257Parse(struct ipcp *ipcp, int argc, char const *const *argv,
258      struct filterent *ofp)
259{
260  int action, proto;
261  int val;
262  char *wp;
263  struct filterent filterdata;
264
265  val = strtol(*argv, &wp, 0);
266  if (*argv == wp || val > MAXFILTERS) {
267    log_Printf(LogWARN, "Parse: invalid filter number.\n");
268    return (0);
269  }
270  if (val < 0) {
271    for (val = 0; val < MAXFILTERS; val++) {
272      ofp->action = A_NONE;
273      ofp++;
274    }
275    log_Printf(LogWARN, "Parse: filter cleared.\n");
276    return (1);
277  }
278  ofp += val;
279
280  if (--argc == 0) {
281    log_Printf(LogWARN, "Parse: missing action.\n");
282    return (0);
283  }
284  argv++;
285
286  proto = P_NONE;
287  memset(&filterdata, '\0', sizeof filterdata);
288
289  if (!strcmp(*argv, "permit")) {
290    action = A_PERMIT;
291  } else if (!strcmp(*argv, "deny")) {
292    action = A_DENY;
293  } else if (!strcmp(*argv, "clear")) {
294    ofp->action = A_NONE;
295    return (1);
296  } else {
297    log_Printf(LogWARN, "Parse: bad action: %s\n", *argv);
298    return (0);
299  }
300  filterdata.action = action;
301
302  argc--;
303  argv++;
304
305  if (filterdata.action == A_DENY) {
306    if (!strcmp(*argv, "host")) {
307      filterdata.action |= A_UHOST;
308      argc--;
309      argv++;
310    } else if (!strcmp(*argv, "port")) {
311      filterdata.action |= A_UPORT;
312      argc--;
313      argv++;
314    }
315  }
316  proto = filter_Nam2Proto(argc, argv);
317  if (proto == P_NONE) {
318    if (ParseAddr(ipcp, argc, argv, &filterdata.saddr, &filterdata.smask,
319                  &filterdata.swidth)) {
320      argc--;
321      argv++;
322      proto = filter_Nam2Proto(argc, argv);
323      if (proto == P_NONE) {
324	if (ParseAddr(ipcp, argc, argv, &filterdata.daddr, &filterdata.dmask,
325                      &filterdata.dwidth)) {
326	  argc--;
327	  argv++;
328	}
329	proto = filter_Nam2Proto(argc, argv);
330	if (proto != P_NONE) {
331	  argc--;
332	  argv++;
333	}
334      } else {
335	argc--;
336	argv++;
337      }
338    } else {
339      log_Printf(LogWARN, "Parse: Address/protocol expected.\n");
340      return (0);
341    }
342  } else {
343    argc--;
344    argv++;
345  }
346
347  val = 1;
348  filterdata.proto = proto;
349
350  switch (proto) {
351  case P_TCP:
352    val = ParseUdpOrTcp(argc, argv, P_TCP, &filterdata);
353    break;
354  case P_UDP:
355    val = ParseUdpOrTcp(argc, argv, P_UDP, &filterdata);
356    break;
357  case P_ICMP:
358    val = ParseIcmp(argc, argv, &filterdata);
359    break;
360  }
361
362  log_Printf(LogDEBUG, "Parse: Src: %s\n", inet_ntoa(filterdata.saddr));
363  log_Printf(LogDEBUG, "Parse: Src mask: %s\n", inet_ntoa(filterdata.smask));
364  log_Printf(LogDEBUG, "Parse: Dst: %s\n", inet_ntoa(filterdata.daddr));
365  log_Printf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(filterdata.dmask));
366  log_Printf(LogDEBUG, "Parse: Proto = %d\n", proto);
367
368  log_Printf(LogDEBUG, "Parse: src:  %s (%d)\n",
369            filter_Op2Nam(filterdata.opt.srcop), filterdata.opt.srcport);
370  log_Printf(LogDEBUG, "Parse: dst:  %s (%d)\n",
371            filter_Op2Nam(filterdata.opt.dstop), filterdata.opt.dstport);
372  log_Printf(LogDEBUG, "Parse: estab: %u\n", filterdata.opt.estab);
373  log_Printf(LogDEBUG, "Parse: syn: %u\n", filterdata.opt.syn);
374  log_Printf(LogDEBUG, "Parse: finrst: %u\n", filterdata.opt.finrst);
375
376  if (val)
377    *ofp = filterdata;
378  return (val);
379}
380
381int
382filter_Set(struct cmdargs const *arg)
383{
384  struct filter *filter;
385
386  if (arg->argc < arg->argn+2)
387    return -1;
388
389  if (!strcmp(arg->argv[arg->argn], "in"))
390    filter = &arg->bundle->filter.in;
391  else if (!strcmp(arg->argv[arg->argn], "out"))
392    filter = &arg->bundle->filter.out;
393  else if (!strcmp(arg->argv[arg->argn], "dial"))
394    filter = &arg->bundle->filter.dial;
395  else if (!strcmp(arg->argv[arg->argn], "alive"))
396    filter = &arg->bundle->filter.alive;
397  else {
398    log_Printf(LogWARN, "filter_Set: %s: Invalid filter name.\n",
399              arg->argv[arg->argn]);
400    return -1;
401  }
402
403  Parse(&arg->bundle->ncp.ipcp, arg->argc - arg->argn - 1,
404        arg->argv + arg->argn + 1, filter->rule);
405  return 0;
406}
407
408const char *
409filter_Action2Nam(int act)
410{
411  static const char *actname[] = { "none   ", "permit ", "deny   " };
412  return actname[act & (A_PERMIT|A_DENY)];
413}
414
415static void
416doShowFilter(struct filterent *fp, struct prompt *prompt)
417{
418  int n;
419
420  for (n = 0; n < MAXFILTERS; n++, fp++) {
421    if (fp->action != A_NONE) {
422      prompt_Printf(prompt, "  %2d %s", n, filter_Action2Nam(fp->action));
423      if (fp->action & A_UHOST)
424        prompt_Printf(prompt, "host ");
425      else if (fp->action & A_UPORT)
426        prompt_Printf(prompt, "port ");
427      else
428        prompt_Printf(prompt, "     ");
429      prompt_Printf(prompt, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
430      prompt_Printf(prompt, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
431      if (fp->proto) {
432	prompt_Printf(prompt, "%s", filter_Proto2Nam(fp->proto));
433
434	if (fp->opt.srcop)
435	  prompt_Printf(prompt, " src %s %d", filter_Op2Nam(fp->opt.srcop),
436		  fp->opt.srcport);
437	if (fp->opt.dstop)
438	  prompt_Printf(prompt, " dst %s %d", filter_Op2Nam(fp->opt.dstop),
439		  fp->opt.dstport);
440	if (fp->opt.estab)
441	  prompt_Printf(prompt, " estab");
442	if (fp->opt.syn)
443	  prompt_Printf(prompt, " syn");
444	if (fp->opt.finrst)
445	  prompt_Printf(prompt, " finrst");
446      }
447      prompt_Printf(prompt, "\n");
448    }
449  }
450}
451
452int
453filter_Show(struct cmdargs const *arg)
454{
455  if (arg->argc > arg->argn+1)
456    return -1;
457
458  if (arg->argc == arg->argn+1) {
459    struct filter *filter;
460
461    if (!strcmp(arg->argv[arg->argn], "in"))
462      filter = &arg->bundle->filter.in;
463    else if (!strcmp(arg->argv[arg->argn], "out"))
464      filter = &arg->bundle->filter.out;
465    else if (!strcmp(arg->argv[arg->argn], "dial"))
466      filter = &arg->bundle->filter.dial;
467    else if (!strcmp(arg->argv[arg->argn], "alive"))
468      filter = &arg->bundle->filter.alive;
469    else
470      return -1;
471    doShowFilter(filter->rule, arg->prompt);
472  } else {
473    struct filter *filter[4];
474    int f;
475
476    filter[0] = &arg->bundle->filter.in;
477    filter[1] = &arg->bundle->filter.out;
478    filter[2] = &arg->bundle->filter.dial;
479    filter[3] = &arg->bundle->filter.alive;
480    for (f = 0; f < 4; f++) {
481      if (f)
482        prompt_Printf(arg->prompt, "\n");
483      prompt_Printf(arg->prompt, "%s:\n", filter[f]->name);
484      doShowFilter(filter[f]->rule, arg->prompt);
485    }
486  }
487
488  return 0;
489}
490
491static const char *protoname[] = { "none", "tcp", "udp", "icmp" };
492
493const char *
494filter_Proto2Nam(int proto)
495{
496  if (proto >= sizeof protoname / sizeof protoname[0])
497    return "unknown";
498  return protoname[proto];
499}
500
501static int
502filter_Nam2Proto(int argc, char const *const *argv)
503{
504  int proto;
505
506  if (argc == 0)
507    proto = 0;
508  else
509    for (proto = sizeof protoname / sizeof protoname[0] - 1; proto; proto--)
510      if (!strcasecmp(*argv, protoname[proto]))
511        break;
512
513  return proto;
514}
515
516static const char *opname[] = {"none", "eq", "gt", "unknown", "lt"};
517
518const char *
519filter_Op2Nam(int op)
520{
521  if (op >= sizeof opname / sizeof opname[0])
522    return "unknown";
523  return opname[op];
524
525}
526
527static int
528filter_Nam2Op(const char *cp)
529{
530  int op;
531
532  for (op = sizeof opname / sizeof opname[0] - 1; op; op--)
533    if (!strcasecmp(cp, opname[op]))
534      break;
535
536  return op;
537}
538