xref: /freebsd/usr.sbin/ppp/filter.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
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 
59 static int filter_Nam2Proto(int, char const *const *);
60 static int filter_Nam2Op(const char *);
61 
62 static 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 
74 int
75 ParseAddr(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 
130 static int
131 ParsePort(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  */
165 static int
166 ParseIcmp(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  */
199 static int
200 ParseUdpOrTcp(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 
252 static int
253 Parse(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 
377 int
378 filter_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 
404 const char *
405 filter_Action2Nam(int act)
406 {
407   static const char *actname[] = { "none   ", "permit ", "deny   " };
408   return actname[act & (A_PERMIT|A_DENY)];
409 }
410 
411 static void
412 doShowFilter(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 
448 int
449 filter_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 
487 static const char *protoname[] = { "none", "tcp", "udp", "icmp" };
488 
489 const char *
490 filter_Proto2Nam(int proto)
491 {
492   if (proto >= sizeof protoname / sizeof protoname[0])
493     return "unknown";
494   return protoname[proto];
495 }
496 
497 static int
498 filter_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 
512 static const char *opname[] = {"none", "eq", "gt", "unknown", "lt"};
513 
514 const char *
515 filter_Op2Nam(int op)
516 {
517   if (op >= sizeof opname / sizeof opname[0])
518     return "unknown";
519   return opname[op];
520 
521 }
522 
523 static int
524 filter_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