filter.c revision 28679
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.12 1997/07/27 23:26:23 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(int argc,
58	  char **argv,
59	  struct in_addr * paddr,
60	  struct in_addr * pmask,
61	  int *pwidth)
62{
63  int bits;
64  char *cp, *wp;
65
66  if (argc < 1) {
67    LogPrintf(LogWARN, "ParseAddr: address/mask is expected.\n");
68    return (0);
69  }
70  pmask->s_addr = 0xffffffff;	/* Assume 255.255.255.255 as default */
71  cp = index(*argv, '/');
72  if (cp)
73    *cp++ = '\0';
74  if (strcasecmp(*argv, "HISADDR") == 0)
75    *paddr = IpcpInfo.his_ipaddr;
76  else if (strcasecmp(*argv, "MYADDR") == 0)
77    *paddr = IpcpInfo.want_ipaddr;
78  else
79    paddr->s_addr = inet_addr(*argv);
80  if (cp && *cp) {
81    bits = strtol(cp, &wp, 0);
82    if (cp == wp || bits < 0 || bits > 32) {
83      LogPrintf(LogWARN, "ParseAddr: bad mask width.\n");
84      return (0);
85    }
86  } else {
87    /* if width is not given, assume whole 32 bits are meaningfull */
88    bits = 32;
89  }
90
91  *pwidth = bits;
92  pmask->s_addr = htonl(netmasks[bits]);
93
94  return (1);
95}
96
97static int
98ParseProto(int argc, char **argv)
99{
100  int proto;
101
102  if (argc < 1)
103    return (P_NONE);
104
105  if (STREQ(*argv, "tcp"))
106    proto = P_TCP;
107  else if (STREQ(*argv, "udp"))
108    proto = P_UDP;
109  else if (STREQ(*argv, "icmp"))
110    proto = P_ICMP;
111  else
112    proto = P_NONE;
113  return (proto);
114}
115
116static int
117ParsePort(char *service, int proto)
118{
119  char *protocol_name, *cp;
120  struct servent *servent;
121  int port;
122
123  switch (proto) {
124  case P_UDP:
125    protocol_name = "udp";
126    break;
127  case P_TCP:
128    protocol_name = "tcp";
129    break;
130  default:
131    protocol_name = 0;
132  }
133
134  servent = getservbyname(service, protocol_name);
135  if (servent != 0)
136    return (ntohs(servent->s_port));
137
138  port = strtol(service, &cp, 0);
139  if (cp == service) {
140    LogPrintf(LogWARN, "ParsePort: %s is not a port name or number.\n",
141	      service);
142    return (0);
143  }
144  return (port);
145}
146
147/*
148 *	ICMP Syntax:	src eq icmp_message_type
149 */
150static int
151ParseIcmp(int argc, char **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    LogPrintf(LogWARN, "ParseIcmp: 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	LogPrintf(LogWARN, "ParseIcmp: 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(char *cp)
181{
182  int op = OP_NONE;
183
184  if (STREQ(cp, "eq"))
185    op = OP_EQ;
186  else if (STREQ(cp, "gt"))
187    op = OP_GT;
188  else if (STREQ(cp, "lt"))
189    op = OP_LT;
190  return (op);
191}
192
193/*
194 *	UDP Syntax: [src op port] [dst op port]
195 */
196static int
197ParseUdpOrTcp(int argc, char **argv, int proto)
198{
199  if (argc == 0) {
200    /* permit/deny all tcp traffic */
201    filterdata.opt.srcop = filterdata.opt.dstop = A_NONE;
202    return (1);
203  }
204  if (argc < 3) {
205    LogPrintf(LogWARN, "ParseUdpOrTcp: bad udp/tcp syntax.\n");
206    return (0);
207  }
208  if (argc >= 3 && STREQ(*argv, "src")) {
209    filterdata.opt.srcop = ParseOp(argv[1]);
210    if (filterdata.opt.srcop == OP_NONE) {
211      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
212      return (0);
213    }
214    filterdata.opt.srcport = ParsePort(argv[2], proto);
215    if (filterdata.opt.srcport == 0)
216      return (0);
217    argc -= 3;
218    argv += 3;
219    if (argc == 0)
220      return (1);
221  }
222  if (argc >= 3 && STREQ(argv[0], "dst")) {
223    filterdata.opt.dstop = ParseOp(argv[1]);
224    if (filterdata.opt.dstop == OP_NONE) {
225      LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
226      return (0);
227    }
228    filterdata.opt.dstport = ParsePort(argv[2], proto);
229    if (filterdata.opt.dstport == 0)
230      return (0);
231    argc -= 3;
232    argv += 3;
233    if (argc == 0)
234      return (1);
235  }
236  if (argc == 1) {
237    if (STREQ(*argv, "estab")) {
238      filterdata.opt.estab = 1;
239      return (1);
240    }
241    LogPrintf(LogWARN, "ParseUdpOrTcp: estab is expected: %s\n", *argv);
242    return (0);
243  }
244  if (argc > 0)
245    LogPrintf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
246  return (0);
247}
248
249char *opname[] = {"none", "eq", "gt", "lt"};
250
251static int
252Parse(int argc, char **argv, struct filterent * ofp)
253{
254  int action, proto;
255  int val;
256  char *wp;
257  struct filterent *fp = &filterdata;
258
259  val = strtol(*argv, &wp, 0);
260  if (*argv == wp || val > MAXFILTERS) {
261    LogPrintf(LogWARN, "Parse: invalid filter number.\n");
262    return (0);
263  }
264  if (val < 0) {
265    for (val = 0; val < MAXFILTERS; val++) {
266      ofp->action = A_NONE;
267      ofp++;
268    }
269    LogPrintf(LogWARN, "Parse: filter cleared.\n");
270    return (1);
271  }
272  ofp += val;
273
274  if (--argc == 0) {
275    LogPrintf(LogWARN, "Parse: missing action.\n");
276    return (0);
277  }
278  argv++;
279
280  proto = P_NONE;
281  bzero(&filterdata, sizeof(filterdata));
282
283  if (STREQ(*argv, "permit")) {
284    action = A_PERMIT;
285  } else if (STREQ(*argv, "deny")) {
286    action = A_DENY;
287  } else if (STREQ(*argv, "clear")) {
288    ofp->action = A_NONE;
289    return (1);
290  } else {
291    LogPrintf(LogWARN, "Parse: bad action: %s\n", *argv);
292    return (0);
293  }
294  fp->action = action;
295
296  argc--;
297  argv++;
298
299  if (fp->action == A_DENY) {
300    if (STREQ(*argv, "host")) {
301      fp->action |= A_UHOST;
302      argc--;
303      argv++;
304    } else if (STREQ(*argv, "port")) {
305      fp->action |= A_UPORT;
306      argc--;
307      argv++;
308    }
309  }
310  proto = ParseProto(argc, argv);
311  if (proto == P_NONE) {
312    if (ParseAddr(argc, argv, &fp->saddr, &fp->smask, &fp->swidth)) {
313      argc--;
314      argv++;
315      proto = ParseProto(argc, argv);
316      if (proto == P_NONE) {
317	if (ParseAddr(argc, argv, &fp->daddr, &fp->dmask, &fp->dwidth)) {
318	  argc--;
319	  argv++;
320	}
321	proto = ParseProto(argc, argv);
322	if (proto) {
323	  argc--;
324	  argv++;
325	}
326      } else {
327	argc--;
328	argv++;
329      }
330    } else {
331      LogPrintf(LogWARN, "Parse: Address/protocol expected.\n");
332      return (0);
333    }
334  } else {
335    argc--;
336    argv++;
337  }
338
339  val = 1;
340  fp->proto = proto;
341
342  switch (proto) {
343  case P_TCP:
344    val = ParseUdpOrTcp(argc, argv, P_TCP);
345    break;
346  case P_UDP:
347    val = ParseUdpOrTcp(argc, argv, P_UDP);
348    break;
349  case P_ICMP:
350    val = ParseIcmp(argc, argv);
351    break;
352  }
353
354  LogPrintf(LogDEBUG, "Parse: Src: %s", inet_ntoa(fp->saddr));
355  LogPrintf(LogDEBUG, "Parse: Src mask: %s ", inet_ntoa(fp->smask));
356  LogPrintf(LogDEBUG, "Parse: Dst: %s", inet_ntoa(fp->daddr));
357  LogPrintf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(fp->dmask));
358  LogPrintf(LogDEBUG, "Parse: Proto = %d\n", proto);
359
360  LogPrintf(LogDEBUG, "Parse: src:  %s (%d)\n", opname[fp->opt.srcop],
361	    fp->opt.srcport);
362  LogPrintf(LogDEBUG, "Parse: dst:  %s (%d)\n", opname[fp->opt.dstop],
363	    fp->opt.dstport);
364  LogPrintf(LogDEBUG, "Parse: estab: %d\n", fp->opt.estab);
365
366  if (val)
367    *ofp = *fp;
368  return (val);
369}
370
371int
372SetIfilter(struct cmdtab * list, int argc, char **argv)
373{
374  if (argc > 0) {
375    (void) Parse(argc, argv, ifilters);
376    return 0;
377  }
378  return -1;
379}
380
381int
382SetOfilter(struct cmdtab * list, int argc, char **argv)
383{
384  if (argc > 0) {
385    (void) Parse(argc, argv, ofilters);
386    return 0;
387  }
388  return -1;
389}
390
391int
392SetDfilter(struct cmdtab * list, int argc, char **argv)
393{
394  if (argc > 0) {
395    (void) Parse(argc, argv, dfilters);
396    return 0;
397  }
398  return -1;
399}
400
401int
402SetAfilter(struct cmdtab * list, int argc, char **argv)
403{
404  if (argc > 0) {
405    (void) Parse(argc, argv, afilters);
406    return 0;
407  }
408  return -1;
409}
410
411static char *protoname[] = {
412  "none", "tcp", "udp", "icmp",
413};
414
415static char *actname[] = {
416  "none   ", "permit ", "deny   ",
417};
418
419static void
420ShowFilter(struct filterent * fp)
421{
422  int n;
423
424  if (!VarTerm)
425    return;
426
427  for (n = 0; n < MAXFILTERS; n++, fp++) {
428    if (fp->action != A_NONE) {
429      fprintf(VarTerm, "%2d %s", n, actname[fp->action]);
430      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
431      fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
432      if (fp->proto) {
433	fprintf(VarTerm, "%s", protoname[fp->proto]);
434
435	if (fp->opt.srcop)
436	  fprintf(VarTerm, " src %s %d", opname[fp->opt.srcop],
437		  fp->opt.srcport);
438	if (fp->opt.dstop)
439	  fprintf(VarTerm, " dst %s %d", opname[fp->opt.dstop],
440		  fp->opt.dstport);
441	if (fp->opt.estab)
442	  fprintf(VarTerm, " estab");
443
444      }
445      fprintf(VarTerm, "\n");
446    }
447  }
448}
449
450int
451ShowIfilter(struct cmdtab * list, int argc, char **argv)
452{
453  ShowFilter(ifilters);
454  return 0;
455}
456
457int
458ShowOfilter(struct cmdtab * list, int argc, char **argv)
459{
460  ShowFilter(ofilters);
461  return 0;
462}
463
464int
465ShowDfilter(struct cmdtab * list, int argc, char **argv)
466{
467  ShowFilter(dfilters);
468  return 0;
469}
470
471int
472ShowAfilter(struct cmdtab * list, int argc, char **argv)
473{
474  ShowFilter(afilters);
475  return 0;
476}
477