perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / lib / filters.c
1 /*
2  * filters.c    Routines to parse Ascend's filter attributes.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19  *
20  * Copyright 2003  The FreeRADIUS server project
21  */
22
23 static const char rcsid[] = "$Id$";
24
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28
29 #include "libradius.h"
30
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34
35 /*
36  * Two types of filters are supported, GENERIC and IP.  The identifiers
37  * are:
38  */
39
40 #define RAD_FILTER_GENERIC      0
41 #define RAD_FILTER_IP           1
42 #define RAD_FILTER_IPX          2
43
44 /*
45  * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
46  * starting at some offset.  The length is:
47  */
48 #define RAD_MAX_FILTER_LEN      6
49
50 /*
51  * ASCEND extensions for ABINARY filters
52  */
53
54 #define IPX_NODE_ADDR_LEN               6
55
56 #if ! defined( FALSE )
57 # define FALSE          0
58 # define TRUE           (! FALSE)
59 #endif
60
61 /*
62  *      ascend_ip_filter_t
63  *
64  *      The binary format of an IP filter.  ALL fields are stored in
65  *      network byte order.
66  *
67  *      srcip:          The source IP address.
68  *
69  *      dstip:          The destination IP address.
70  *
71  *      srcmask:        The number of leading one bits in the source address
72  *                      mask.  Specifies the bits of interest.
73  *
74  *      dstmask:        The number of leading one bits in the destination
75  *                      address mask. Specifies the bits of interest.
76  *
77  *      proto:          The IP protocol number
78  *
79  *      established:    A boolean value.  TRUE when we care about the
80  *                      established state of a TCP connection.  FALSE when
81  *                      we dont care.
82  *
83  *      srcport:        TCP or UDP source port number.
84  *
85  *      dstport:        TCP or UDP destination port number.
86  *
87  *      srcPortCmp:     One of the values of the RadFilterComparison
88  *                      enumeration, specifying how to compare the
89  *                      srcport value.
90  *
91  *      dstPortCmp:     One of the values of the RadFilterComparison
92  *                      enumeration, specifying how to compare the
93  *                      dstport value.
94  *
95  *      fill:           Round things out to a int16_t boundary.
96  */
97 typedef struct ascend_ip_filter_t {
98         uint32_t        srcip;
99         uint32_t        dstip;
100         uint8_t         srcmask;
101         uint8_t         dstmask;
102         uint8_t         proto;
103         uint8_t         established;
104         uint16_t        srcport;
105         uint16_t        dstport;
106         uint8_t         srcPortComp;
107         uint8_t         dstPortComp;
108         unsigned char   fill[4];        /* used to be fill[2] */
109 } ascend_ip_filter_t;
110
111
112 /*
113  *      ascend_ipx_net_t
114  *
115  *      net:      IPX Net address
116  *
117  *      node:     IPX Node address
118  *
119  *      socket:      IPX socket address
120  */
121 typedef struct ascend_ipx_net_t {
122         uint32_t        net;
123         uint8_t         node[IPX_NODE_ADDR_LEN];
124         uint16_t        socket;
125 } ascend_ipx_net_t;
126
127 /*
128  *      ascend_ipx_filter_t
129  *
130  *      The binary format of an IPX filter.  ALL fields are stored in
131  *      network byte order.
132  *
133  *      src:            Source net, node, and socket.
134  *
135  *      dst:            Destination net, node, and socket.
136  *
137  *      srcSocComp:     Source socket compare value
138  *
139  *      dstSocComp:     Destination socket compare value
140  */
141 typedef struct ascend_ipx_filter_t {
142         ascend_ipx_net_t src;
143         ascend_ipx_net_t dst;
144         uint8_t         srcSocComp;
145         uint8_t         dstSocComp;
146 } ascend_ipx_filter_t;
147
148
149 /*
150  *      ascend_generic_filter_t
151  *
152  *      The binary format of a GENERIC filter.  ALL fields are stored in
153  *      network byte order.
154  *
155  *      offset:         Number of bytes into packet to start comparison.
156  *
157  *      len:            Number of bytes to mask and compare.  May not
158  *                      exceed RAD_MAX_FILTER_LEN.
159  *
160  *      more:           Boolean.  If non-zero the next filter entry is
161  *                      also to be applied to a packet.
162  *
163  *      mask:           A bit mask specifying the bits to compare.
164  *
165  *      value:          A value to compare against the masked bits at
166  *                      offset in a users packet.
167  *
168  *      compNeq:        Defines type of comarison (Equal or Notequal)
169  *                      default is Equal.
170  *
171  *      fill:           Round things out to a dword boundary
172  */
173 typedef struct ascend_generic_filter_t {
174         uint16_t        offset;
175         uint16_t        len;
176         uint16_t        more;
177         uint8_t         mask[ RAD_MAX_FILTER_LEN ];
178         uint8_t         value[ RAD_MAX_FILTER_LEN ];
179         uint8_t         compNeq;
180         uint8_t         fill[3];        /* used to be fill[1] */
181 } ascend_generic_filter_t;
182
183 /*
184  *      ascend_filter_t
185  *
186  *      A binary filter element.  Contains one of ascend_ip_filter_t,
187  *      ascend_ipx_filter_t, or ascend_generic_filter_t.
188  *
189  *      All fields are stored in network byte order.
190  *
191  *      type:           Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
192  *
193  *      forward:        TRUE if we should forward packets that match this
194  *                      filter, FALSE if we should drop packets that match
195  *                      this filter.
196  *
197  *      direction:      TRUE if this is an input filter, FALSE if this is
198  *                      an output filter.
199  *
200  *      fill:           Round things out to a dword boundary.
201  *
202  *      u:              A union of
203  *                      ip:             An ip filter entry
204  *                      generic:        A generic filter entry
205  */
206 typedef struct ascend_filter_t {
207         uint8_t         type;
208         uint8_t         forward;
209         uint8_t         direction;
210         uint8_t         fill;
211         union {
212                 ascend_ip_filter_t       ip;
213                 ascend_ipx_filter_t      ipx;
214                 ascend_generic_filter_t generic;
215         } u;
216 } ascend_filter_t;
217 #define SIZEOF_RADFILTER 32
218
219 /*
220  * FilterPortType:
221  *
222  * Ascii names of some well known tcp/udp services.
223  * Used for filtering on a port type.
224  *
225  * ??? What the heck is wrong with getservbyname?
226  */
227 static const LRAD_NAME_NUMBER filterPortType[] = {
228         { "ftp-data",   20 },
229         { "ftp",        21 },
230         { "telnet",     23 },
231         { "smtp",       25 },
232         { "nameserver", 42 },
233         { "domain",     53 },
234         { "tftp",       69 },
235         { "gopher",     70 },
236         { "finger",     79 },
237         { "www",        80 },
238         { "kerberos",   88 },
239         { "hostname",   101 },
240         { "nntp",       119 },
241         { "ntp",        123 },
242         { "exec",       512 },
243         { "login",      513 },
244         { "cmd",        514 },
245         { "talk",       517 },
246         {  NULL ,       0},
247 };
248
249 static const LRAD_NAME_NUMBER filterType[] = {
250         { "generic",    RAD_FILTER_GENERIC},
251         { "ip",         RAD_FILTER_IP},
252         { "ipx",        RAD_FILTER_IPX},
253         { NULL,         0},
254 };
255
256 typedef enum {
257     FILTER_GENERIC_TYPE,
258     FILTER_IP_TYPE,
259     FILTER_IN,
260     FILTER_OUT,
261     FILTER_FORWARD,
262     FILTER_DROP,
263     FILTER_GENERIC_OFFSET,
264     FILTER_GENERIC_MASK,
265     FILTER_GENERIC_VALUE,
266     FILTER_GENERIC_COMPNEQ,
267     FILTER_GENERIC_COMPEQ,
268     FILTER_MORE,
269     FILTER_IP_DST,
270     FILTER_IP_SRC,
271     FILTER_IP_PROTO,
272     FILTER_IP_DST_PORT,
273     FILTER_IP_SRC_PORT,
274     FILTER_EST,
275     FILTER_IPX_TYPE,
276     FILTER_IPX_DST_IPXNET,
277     FILTER_IPX_DST_IPXNODE,
278     FILTER_IPX_DST_IPXSOCK,
279     FILTER_IPX_SRC_IPXNET,
280     FILTER_IPX_SRC_IPXNODE,
281     FILTER_IPX_SRC_IPXSOCK
282 } FilterTokens;
283
284
285 static const LRAD_NAME_NUMBER filterKeywords[] = {
286         { "ip",         FILTER_IP_TYPE },
287         { "generic",    FILTER_GENERIC_TYPE },
288         { "in",         FILTER_IN },
289         { "out",        FILTER_OUT },
290         { "forward",    FILTER_FORWARD },
291         { "drop",       FILTER_DROP },
292         { "dstip",      FILTER_IP_DST },
293         { "srcip",      FILTER_IP_SRC },
294         { "dstport",    FILTER_IP_DST_PORT },
295         { "srcport",    FILTER_IP_SRC_PORT },
296         { "est",        FILTER_EST },
297         { "more",       FILTER_MORE },
298         { "!=",         FILTER_GENERIC_COMPNEQ },
299         { "==",         FILTER_GENERIC_COMPEQ  },
300         { "ipx",        FILTER_IPX_TYPE  },
301         { "dstipxnet",  FILTER_IPX_DST_IPXNET  },
302         { "dstipxnode", FILTER_IPX_DST_IPXNODE  },
303         { "dstipxsock", FILTER_IPX_DST_IPXSOCK  },
304         { "srcipxnet",  FILTER_IPX_SRC_IPXNET  },
305         { "srcipxnode", FILTER_IPX_SRC_IPXNODE  },
306         { "srcipxsock", FILTER_IPX_SRC_IPXSOCK  },
307         {  NULL ,       -1},
308 };
309
310 /*
311  * FilterProtoName:
312  *
313  * Ascii name of protocols used for filtering.
314  *
315  *  ??? What the heck is wrong with getprotobyname?
316  */
317 static const LRAD_NAME_NUMBER filterProtoName[] = {
318         { "tcp",  6 },
319         { "udp",  17 },
320         { "ospf", 89 },
321         { "icmp", 1 },
322         { "0",    0 },
323         {  NULL , -1 },
324 };
325
326
327 /*
328  * RadFilterComparison:
329  *
330  * An enumerated values for the IP filter port comparisons.
331  */
332 typedef enum {
333         RAD_NO_COMPARE = 0,
334         RAD_COMPARE_LESS,
335         RAD_COMPARE_EQUAL,
336         RAD_COMPARE_GREATER,
337         RAD_COMPARE_NOT_EQUAL
338 } RadFilterComparison;
339
340 static const LRAD_NAME_NUMBER filterCompare[] = {
341         { "<",  RAD_COMPARE_LESS },
342         { "=",  RAD_COMPARE_EQUAL },
343         { ">",  RAD_COMPARE_GREATER },
344         { "!=", RAD_COMPARE_NOT_EQUAL },
345         { NULL, 0 },
346 };
347
348
349 /*
350  *      String split routine.  Splits an input string IN PLACE
351  *      into pieces, based on spaces.
352  */
353 static int str2argv(char *str, char **argv, int max_argc)
354 {
355         int argc = 0;
356
357         while (*str) {
358                 if (argc >= max_argc) return argc;
359
360                 while (*str == ' ') *(str++) = '\0';
361
362                 if (!*str) return argc;
363
364                 argv[argc] = str;
365                 argc++;
366
367                 while (*str && (*str != ' ')) str++;
368         }
369
370         return argc;
371 }
372
373
374 /*
375  *      hex2bin converts hexadecimal strings into binary
376  *
377  *      Hmm... there are a number of such functions in the source.
378  *      maybe we want to make a library function?
379  */
380 static int hex2bin(const char *str, uint8_t *bin, size_t length)
381 {
382         unsigned int            len;
383         const           char *letters = "0123456789ABCDEFabcdef";
384
385         /*
386          *      Must be byte aligned, not nibble aligned.
387          */
388         len = strlen(str);
389         if ((len & 0x01) != 0) return -1;
390
391         /*
392          *      Input string is too long to fit.  Don't even bother
393          *      trying.
394          */
395         if ((len / 2) > length) return -1;
396
397         /*
398          *      Input string contains non-hex characters, die.
399          */
400         if (strspn(str, letters) != len) return -1;
401
402         len = 0;
403         while (*str) {
404                 char    *c1, *c2;
405
406                 c1 = memchr(letters, toupper((int) *(str++)), 16);
407                 c2 = memchr(letters, toupper((int) *(str++)), 16);
408
409                 *(bin++) = ((c1-letters)<<4) + (c2-letters);
410                 len++;
411         }
412
413         return len;
414 }
415
416
417 /*
418  *      ascend_parse_ipx_net
419  *
420  *      srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
421  */
422 static int ascend_parse_ipx_net(int argc, char **argv,
423                                 ascend_ipx_net_t *net, uint8_t *comp)
424 {
425         int             token;
426         const char      *p;
427
428         if (argc < 3) return -1;
429
430         /*
431          *      Parse the net, which is a hex number.
432          */
433         net->net = htonl(strtol(argv[0], NULL, 16));
434
435         /*
436          *      Parse the node.
437          */
438         token = lrad_str2int(filterKeywords, argv[1], -1);
439         switch (token) {
440         case FILTER_IPX_SRC_IPXNODE:
441         case FILTER_IPX_DST_IPXNODE:
442                 break;
443
444         default:
445                 return -1;
446         }
447
448         /*
449          *      Can have a leading "0x" or "0X"
450          */
451         p = argv[2];
452         if ((memcmp(p, "0X", 2) == 0) ||
453             (memcmp(p, "0x", 2) == 0)) p += 2;
454
455         /*
456          *      Node must be 6 octets long.
457          */
458         token = hex2bin(p, net->node, IPX_NODE_ADDR_LEN);
459         if (token != IPX_NODE_ADDR_LEN) return -1;
460
461         /*
462          *      Nothing more, die.
463          */
464         if (argc == 3) return 3;
465
466         /*
467          *      Can't be too little or too much.
468          */
469         if (argc != 6) return -1;
470
471         /*
472          *      Parse the socket.
473          */
474         token = lrad_str2int(filterKeywords, argv[3], -1);
475         switch (token) {
476         case FILTER_IPX_SRC_IPXSOCK:
477         case FILTER_IPX_DST_IPXSOCK:
478                 break;
479
480         default:
481                 return -1;
482         }
483
484         /*
485          *      Parse the command "<", ">", "=" or "!="
486          */
487         token = lrad_str2int(filterCompare, argv[4], -1);
488         switch (token) {
489         case RAD_COMPARE_LESS:
490         case RAD_COMPARE_EQUAL:
491         case RAD_COMPARE_GREATER:
492         case RAD_COMPARE_NOT_EQUAL:
493                 *comp = token;
494                 break;
495
496         default:
497                 return -1;
498         }
499
500         /*
501          *      Parse the value.
502          */
503         token = strtoul(argv[5], NULL, 16);
504         if (token > 65535) return -1;
505
506         net->socket = token;
507         net->socket = htons(net->socket);
508
509
510         /*
511          *      Everything's OK, we parsed 6 entries.
512          */
513         return 6;
514 }
515
516 /*
517  *      ascend_parse_ipx_filter
518  *
519  *      This routine parses an IPX filter string from a string.
520  *      The format of the string is:
521  *
522  *      [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
523  *      [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
524  *
525  * Fields in [...] are optional.
526  *      where:
527  *
528  *  srcipxnet:      Keyword for source IPX address.
529  *                  nnnn = IPX Node address.
530  *
531  *  srcipxnode:     Keyword for source IPX Node address.
532  *                  mmmmm = IPX Node Address, could be FFFFFF.
533  *                  A vlid ipx node number should accompany ipx net number.
534  *
535  *      srcipxsoc:      Keyword for source IPX socket address.
536  *
537  *      cmd:            One of ">" or "<" or "=" or "!=".
538  *
539  *      value:          Socket value to be compared against, in hex.
540  *
541  *      dstipxnet:      Keyword for destination IPX address.
542  *                      nnnn = IPX Node address.
543  *
544  *      dstipxnode:     Keyword for destination IPX Node address.
545  *              mmmmm = IPX Node Address, could be FFFFFF.
546  *                     A valid ipx node number should accompany ipx net number.
547  *
548  *      dstipxsoc:      Keyword for destination IPX socket address.
549  *
550  *      cmd:            One of ">" or "<" or "=" or "!=".
551  *
552  *      value:          Socket value to be compared against, in hex.
553  */
554 static int ascend_parse_ipx(int argc, char **argv, ascend_ipx_filter_t *filter)
555 {
556         int rcode;
557         int token;
558         int flags = 0;
559
560         /*
561          *      We may have nothing, in which case we simply return.
562          */
563         if (argc == 0) return 0;
564
565         /*
566          *      Must have "net N node M"
567          */
568         if (argc < 4) return -1;
569
570         while ((argc > 0) && (flags != 0x03)) {
571                 token = lrad_str2int(filterKeywords, argv[0], -1);
572                 switch (token) {
573                 case FILTER_IPX_SRC_IPXNET:
574                         if (flags & 0x01) return -1;
575                         rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
576                                                      &(filter->src),
577                                                      &(filter->srcSocComp));
578                         if (rcode < 0) return -1;
579                         argc -= (rcode + 1);
580                         argv += rcode + 1;
581                         flags |= 0x01;
582                         break;
583
584                 case FILTER_IPX_DST_IPXNET:
585                         if (flags & 0x02) return -1;
586                         rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
587                                                      &(filter->dst),
588                                                      &(filter->dstSocComp));
589                         if (rcode < 0) return -1;
590                         argc -= (rcode + 1);
591                         argv += rcode + 1;
592                         flags |= 0x02;
593                         break;
594
595                 default:
596                         librad_log("Unknown string \"%s\" in IPX data filter",
597                                    argv[0]);
598                         return -1;
599                 }
600         }
601
602         /*
603          *      Arguments left over: die.
604          */
605         if (argc != 0) return -1;
606
607         /*
608          *      Everything's OK.
609          */
610         return 0;
611 }
612
613
614 /*
615  *      Parse an IP address and optionally a netmask, to a uint32_t.
616  *
617  *      ipaddr should already be initialized to zero.
618  *      ipaddr is in network byte order.
619  *
620  *      Returns -1 on error, or the number of bits in the netmask, otherwise.
621  */
622 static int ascend_parse_ipaddr(uint32_t *ipaddr, char *str)
623 {
624         int             count = 0;
625         int             ip[4];
626         int             masklen;
627         uint32_t        netmask = 0;
628
629         /*
630          *      Look for IP's.
631          */
632         count = 0;
633         while (*str && (count < 4) && (netmask == 0)) {
634         next:
635                 ip[count] = 0;
636
637                 while (*str) {
638                         switch (*str) {
639                         case '0': case '1': case '2': case '3':
640                         case '4': case '5': case '6': case '7':
641                         case '8': case '9':
642                                 ip[count] *= 10;
643                                 ip[count] += (*str) - '0';
644                                 str++;
645                                 break;
646
647
648                         case '.': /* dot between IP numbers. */
649                                 str++;
650                                 if (ip[count] > 255) return -1;
651
652                                 /*
653                                  *      24, 16, 8, 0, done.
654                                  */
655                                 *ipaddr |= (ip[count] << (8 * (3 - count)));
656                                 count++;
657                                 goto next;
658
659                         case '/': /* netmask  */
660                                 str++;
661                                 masklen = atoi(str);
662                                 if ((masklen < 0) || (masklen > 32)) return -1;
663                                 str += strspn(str, "0123456789");
664                                 netmask = masklen;
665                                 goto finalize;
666                                 break;
667
668                         default:
669                                 librad_log("Invalid character in IP address");
670                                 return -1;
671                         }
672                 } /* loop over one character */
673         } /* loop until the count hits 4 */
674
675         if (count == 3) {
676         finalize:
677                 /*
678                  *      Do the last one, too.
679                  */
680                 if (ip[count] > 255) return -1;
681
682                 /*
683                  *      24, 16, 8, 0, done.
684                  */
685                 *ipaddr |= (ip[count] << (8 * (3 - count)));
686         }
687
688         /*
689          *      We've hit the end of the IP address, and there's something
690          *      else left over: die.
691          */
692         if (*str) return -1;
693
694         /*
695          *      Set the default netmask.
696          */
697         if (!netmask) {
698                 if (!*ipaddr) {
699                         netmask = 0;
700                 } else if ((*ipaddr & 0x80000000) == 0) {
701                         netmask = 8;
702                 } else if ((*ipaddr & 0xc0000000) == 0x80000000) {
703                         netmask = 16;
704                 } else if ((*ipaddr & 0xe0000000) == 0xc0000000) {
705                         netmask = 24;
706                 } else {
707                         netmask = 32;
708                 }
709         }
710
711         *ipaddr = htonl(*ipaddr);
712         return netmask;
713 }
714
715 /*
716  *      ascend_parse_port:  Parse a comparator and port.
717  *
718  *      Returns -1 on error, or the comparator.
719  */
720 static int ascend_parse_port(uint16_t *port, char *compare, char *str)
721 {
722         int rcode, token = -1;
723
724         /*
725          *      There MUST be a comparison string.
726          */
727         rcode = lrad_str2int(filterCompare, compare, -1);
728         if (rcode < 0) return rcode;
729
730         if (strspn(str, "0123456789") == strlen(str)) {
731                 token = atoi(str);
732         } else {
733                 token = lrad_str2int(filterPortType, str, -1);
734         }
735
736         if ((token < 0) || (token > 65535)) return -1;
737
738         *port = token;
739         *port = htons(*port);
740
741         return rcode;
742 }
743
744
745 /*
746  *      ascend_parse_ip:
747  *
748  *      This routine parses an IP filter string from a RADIUS
749  *      reply. The format of the string is:
750  *
751  *      ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
752  *          [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
753  *
754  *      Fields in [...] are optional.
755  *
756  *      dstip:          Keyword for destination IP address.
757  *                      n.n.n.n = IP address. /nn - netmask.
758  *
759  *      srcip:          Keyword for source IP address.
760  *                      n.n.n.n = IP address. /nn - netmask.
761  *
762  *      proto:          Optional protocol field. Either a name or
763  *                      number. Known names are in FilterProtoName[].
764  *
765  *      dstport:        Keyword for destination port. Only valid with tcp
766  *                      or udp. 'cmp' are in FilterPortType[]. 'value' can be
767  *                      a name or number.
768  *
769  *      srcport:        Keyword for source port. Only valid with tcp
770  *                      or udp. 'cmp' are in FilterPortType[]. 'value' can be
771  *                      a name or number.
772  *
773  *      est:            Keyword for TCP established. Valid only for tcp.
774  *
775  */
776 static int ascend_parse_ip(int argc, char **argv, ascend_ip_filter_t *filter)
777 {
778         int rcode;
779         int token;
780         int flags;
781
782         /*
783          *      We may have nothing, in which case we simply return.
784          */
785         if (argc == 0) return 0;
786
787         /*
788          *      There may, or may not, be src & dst IP's in the string.
789          */
790         flags = 0;
791         while ((argc > 0) && (flags != 7)) {
792                 token = lrad_str2int(filterKeywords, argv[0], -1);
793                 switch (token) {
794                 case FILTER_IP_SRC:
795                         if (flags & 0x01) return -1;
796                         if (argc < 2) return -1;
797
798                         rcode = ascend_parse_ipaddr(&filter->srcip, argv[1]);
799                         if (rcode < 0) return rcode;
800
801                         filter->srcmask = rcode;
802                         flags |= 0x01;
803                         argv += 2;
804                         argc -= 2;
805                         break;
806
807                 case FILTER_IP_DST:
808                         if (flags & 0x02) return -1;
809                         if (argc < 2) return -1;
810
811                         rcode = ascend_parse_ipaddr(&filter->dstip, argv[1]);
812                         if (rcode < 0) return rcode;
813
814                         filter->dstmask = rcode;
815                         flags |= 0x02;
816                         argv += 2;
817                         argc -= 2;
818                         break;
819
820                         /*
821                          *      Should be protocol, ASCII or otherwise.
822                          */
823                 default:
824                         if (strspn(argv[0], "0123456789") == strlen(argv[0])) {
825                                 token = atoi(argv[0]);
826                         } else {
827                                 token = lrad_str2int(filterProtoName, argv[0], -1);
828                                 if (token == -1) {
829                                         librad_log("Unknown IP protocol \"%s\" in IP data filter",
830                                                    argv[0]);
831                                         return -1;
832                                 }
833                         }
834                         filter->proto = token;
835                         flags = 0x07; /* MUST have parsed everything. */
836
837                         argv++;
838                         argc--;
839                         break;
840                 }
841         } /* looking for src/dst IP, and proto */
842
843         /*
844          *      Done looking for everything, return.
845          */
846         if (argc == 0) return 0;
847
848         /*
849          *      There may, or may not, be src & dst ports in the string.
850          */
851         flags = 0;
852         while ((argc > 0) && (flags != 7)) {
853                 token = lrad_str2int(filterKeywords, argv[0], -1);
854                 switch (token) {
855                 case FILTER_IP_SRC_PORT:
856                         if (flags & 0x01) return -1;
857                         if (argc < 3) return -1;
858
859                         rcode = ascend_parse_port(&filter->srcport,
860                                                   argv[1], argv[2]);
861                         if (rcode < 0) return rcode;
862                         filter->srcPortComp = rcode;
863
864                         flags |= 0x01;
865                         argv += 3;
866                         argc -= 3;
867                         break;
868
869                 case FILTER_IP_DST_PORT:
870                         if (flags & 0x02) return -1;
871                         if (argc < 3) return -1;
872
873                         rcode = ascend_parse_port(&filter->dstport,
874                                                   argv[1], argv[2]);
875                         if (rcode < 0) return rcode;
876                         filter->dstPortComp = rcode;
877
878                         flags |= 0x02;
879                         argv += 3;
880                         argc -= 3;
881                         break;
882
883                         /*
884                          *      Look for established connections.
885                          */
886                 case FILTER_EST:
887                         filter->established = 1;
888                         argv++;
889                         argc--;
890                         flags = 0x07;
891                         break;
892
893                         /*
894                          *      Unknown thingy.
895                          */
896                 default:
897                         librad_log("Unknown string \"%s\" in IP data filter",
898                                    argv[0]);
899                         return -1;
900                         break;
901                 }
902         } /* looking for src/dst port */
903
904         /*
905          *      We should have parsed everything by now.
906          */
907         if (argc != 0) {
908                 librad_log("Unknown extra string \"%s\" in IP data filter",
909                            argv[0]);
910                 return -1;
911         }
912
913         return 0;
914 }
915
916
917 /*
918  *      ascend_parse_generic
919  *
920  *      This routine parses a Generic filter string from a RADIUS
921  *      reply. The format of the string is:
922  *
923  *      generic dir action offset mask value [== or != ] [more]
924  *
925  *      Fields in [...] are optional.
926  *
927  *      offset:         A Number. Specifies an offset into a frame
928  *                      to start comparing.
929  *
930  *      mask:           A hexadecimal mask of bits to compare.
931  *
932  *      value:          A value to compare with the masked data.
933  *
934  *      compNeq:        Defines type of comparison. ( "==" or "!=")
935  *                      Default is "==".
936  *
937  *      more:           Optional keyword MORE, to represent the attachment
938  *                      to the next entry.
939  */
940 static int ascend_parse_generic(int argc, char **argv,
941                                 ascend_generic_filter_t *filter)
942 {
943         int rcode;
944         int token;
945         int flags;
946
947         /*
948          *      We may have nothing, in which case we simply return.
949          */
950         if (argc == 0) return 0;
951
952         /*
953          *      We need at least "offset mask value"
954          */
955         if (argc < 3) return -1;
956
957         /*
958          *      No more than optional comparison and "more"
959          */
960         if (argc > 5) return -1;
961
962         /*
963          *      Offset is a uint16_t number.
964          */
965         if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;
966
967         rcode = atoi(argv[0]);
968         if (rcode > 65535) return -1;
969
970         filter->offset = rcode;
971         filter->offset = htons(filter->offset);
972
973         rcode = hex2bin(argv[1], filter->mask, sizeof(filter->mask));
974         if (rcode < 0) return -1;
975
976         token = hex2bin(argv[2], filter->value, sizeof(filter->value));
977         if (token < 0) return -1;
978
979         /*
980          *      The mask and value MUST be the same length.
981          */
982         if (rcode != token) return -1;
983
984         filter->len = rcode;
985         filter->len = htons(filter->len);
986
987         /*
988          *      Nothing more.  Exit.
989          */
990         if (argc == 3) return 0;
991
992         argc -= 3;
993         argv += 3;
994         flags = 0;
995
996         while (argc >= 1) {
997                 token = lrad_str2int(filterKeywords, argv[0], -1);
998                 switch (token) {
999                 case FILTER_GENERIC_COMPNEQ:
1000                         if (flags & 0x01) return -1;
1001                         filter->compNeq = TRUE;
1002                         flags |= 0x01;
1003                         break;
1004                 case FILTER_GENERIC_COMPEQ:
1005                         if (flags & 0x01) return -1;
1006                         filter->compNeq = FALSE;
1007                         flags |= 0x01;
1008                         break;
1009
1010                 case FILTER_MORE:
1011                         if (flags & 0x02) return -1;
1012                         filter->more = htons( 1 );
1013                         flags |= 0x02;
1014                         break;
1015
1016                 default:
1017                         librad_log("Invalid string \"%s\" in generic data filter",
1018                                    argv[0]);
1019                         return -1;
1020                 }
1021
1022                 argc--;
1023                 argv++;
1024         }
1025
1026         return 0;
1027 }
1028
1029
1030 /*
1031  * filterBinary:
1032  *
1033  * This routine will call routines to parse entries from an ASCII format
1034  * to a binary format recognized by the Ascend boxes.
1035  *
1036  *      pair:                   Pointer to value_pair to place return.
1037  *
1038  *      valstr:                 The string to parse
1039  *
1040  *      return:                 -1 for error or 0.
1041  */
1042 int
1043 ascend_parse_filter(VALUE_PAIR *pair)
1044 {
1045         int             token, type;
1046         int             rcode;
1047         int             argc;
1048         char            *argv[32];
1049         ascend_filter_t filter;
1050
1051         rcode = -1;
1052
1053         /*
1054          *      Tokenize the input string in the VP.
1055          *
1056          *      Once the filter is *completelty* parsed, then we will
1057          *      over-write it with the final binary filter.
1058          */
1059         argc = str2argv(pair->strvalue, argv, 32);
1060         if (argc < 3) return -1;
1061
1062         /*
1063          *      Decide which filter type it is: ip, ipx, or generic
1064          */
1065         type = lrad_str2int(filterType, argv[0], -1);
1066         memset(&filter, 0, sizeof(filter));
1067
1068         /*
1069          *      Validate the filter type.
1070          */
1071         switch (type) {
1072         case RAD_FILTER_GENERIC:
1073         case RAD_FILTER_IP:
1074         case RAD_FILTER_IPX:
1075                 filter.type = type;
1076                 break;
1077
1078         default:
1079                 librad_log("Unknown Ascend filter type \"%s\"", argv[0]);
1080                 return -1;
1081                 break;
1082         }
1083
1084         /*
1085          *      Parse direction
1086          */
1087         token = lrad_str2int(filterKeywords, argv[1], -1);
1088         switch (token) {
1089         case FILTER_IN:
1090                 filter.direction = 1;
1091                 break;
1092
1093         case FILTER_OUT:
1094                 filter.direction = 0;
1095                 break;
1096
1097         default:
1098                 librad_log("Unknown Ascend filter direction \"%s\"", argv[1]);
1099                 return -1;
1100                 break;
1101         }
1102
1103         /*
1104          *      Parse action
1105          */
1106         token = lrad_str2int(filterKeywords, argv[2], -1);
1107         switch (token) {
1108         case FILTER_FORWARD:
1109                 filter.forward = 1;
1110                 break;
1111
1112         case FILTER_DROP:
1113                 filter.forward = 0;
1114                 break;
1115
1116         default:
1117                 librad_log("Unknown Ascend filter action \"%s\"", argv[2]);
1118                 return -1;
1119                 break;
1120         }
1121
1122
1123         switch (type) {
1124         case RAD_FILTER_GENERIC:
1125                 rcode = ascend_parse_generic(argc - 3, &argv[3],
1126                                           &filter.u.generic);
1127                 break;
1128
1129         case RAD_FILTER_IP:
1130                 rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
1131                 break;
1132
1133         case RAD_FILTER_IPX:
1134                 rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
1135                 break;
1136
1137         default:                /* should never reach here. */
1138                 break;
1139         }
1140
1141         /*
1142          *      Touch the VP only if everything was OK.
1143          */
1144         if (rcode == 0) {
1145                 pair->length = SIZEOF_RADFILTER;
1146                 memcpy(pair->strvalue, &filter, sizeof(filter));
1147         }
1148
1149         return rcode;
1150
1151 #if 0
1152     /*
1153      * if 'more' is set then this new entry must exist, be a
1154      * FILTER_GENERIC_TYPE, direction and disposition must match for
1155      * the previous 'more' to be valid. If any should fail then TURN OFF
1156      * previous 'more'
1157      */
1158     if( prevRadPair ) {
1159         filt = ( RadFilter * )prevRadPair->strvalue;
1160         if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
1161            ( prevRadPair->attribute != pair->attribute ) ||
1162            ( filt->indirection != radFil.indirection ) ||
1163            ( filt->forward != radFil.forward ) ) {
1164             gen = &filt->u.generic;
1165             gen->more = FALSE;
1166             librad_log("filterBinary:  'more' for previous entry doesn't match: %s.\n",
1167                      valstr);
1168         }
1169     }
1170     prevRadPair = NULL;
1171     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
1172         if( radFil.u.generic.more ) {
1173             prevRadPair = pair;
1174         }
1175     }
1176
1177     if( rc != -1 ) {
1178         memcpy( pair->strvalue, (char *) &radFil, pair->length );
1179     }
1180     return(rc);
1181
1182 #endif
1183 }
1184
1185 /*
1186  *      Print an Ascend binary filter attribute to a string,
1187  *      Grrr... Ascend makes the server do this work, instead
1188  *      of doing it on the NAS.
1189  *
1190  *      Note we don't bother checking 'len' after the snprintf's.
1191  *      This function should ONLY be called with a large (~1k) buffer.
1192  */
1193 void print_abinary(VALUE_PAIR *vp, u_char *buffer, int len)
1194 {
1195   int                   i;
1196   char                  *p;
1197   ascend_filter_t       filter;
1198
1199   static const char *action[] = {"drop", "forward"};
1200   static const char *direction[] = {"out", "in"};
1201
1202   p = (char *)buffer;
1203
1204   /*
1205    *  Just for paranoia: wrong size filters get printed as octets
1206    */
1207   if (vp->length > SIZEOF_RADFILTER) {
1208     strcpy(p, "0x");
1209     p += 2;
1210     for (i = 0; i < vp->length; i++) {
1211       sprintf(p, " %02x", vp->strvalue[i]);
1212       p += 3;
1213     }
1214     return;
1215   }
1216
1217   memcpy(&filter, vp->strvalue, SIZEOF_RADFILTER); /* alignment issues */
1218   *(p++) = '"';
1219   len -= 3;                     /* account for leading & trailing quotes */
1220
1221   i = snprintf(p, len, "%s %s %s",
1222                lrad_int2str(filterType, filter.type, "??"),
1223                direction[filter.direction & 0x01],
1224                action[filter.forward & 0x01]);
1225
1226   p += i;
1227   len -= i;
1228
1229   /*
1230    *    Handle IP filters
1231    */
1232   if (filter.type == RAD_FILTER_IP) {
1233
1234     if (filter.u.ip.srcip) {
1235       i = snprintf(p, len, " srcip %d.%d.%d.%d/%d",
1236                    ((u_char *) &filter.u.ip.srcip)[0],
1237                    ((u_char *) &filter.u.ip.srcip)[1],
1238                    ((u_char *) &filter.u.ip.srcip)[2],
1239                    ((u_char *) &filter.u.ip.srcip)[3],
1240                    filter.u.ip.srcmask);
1241       p += i;
1242       len -= i;
1243     }
1244
1245     if (filter.u.ip.dstip) {
1246       i = snprintf(p, len, " dstip %d.%d.%d.%d/%d",
1247                    ((u_char *) &filter.u.ip.dstip)[0],
1248                    ((u_char *) &filter.u.ip.dstip)[1],
1249                    ((u_char *) &filter.u.ip.dstip)[2],
1250                    ((u_char *) &filter.u.ip.dstip)[3],
1251                    filter.u.ip.dstmask);
1252       p += i;
1253       len -= i;
1254     }
1255
1256     i =  snprintf(p, len, " %s",
1257                   lrad_int2str(filterProtoName, filter.u.ip.proto, "??"));
1258     p += i;
1259     len -= i;
1260
1261     if (filter.u.ip.srcPortComp > RAD_NO_COMPARE) {
1262       i = snprintf(p, len, " srcport %s %d",
1263                    lrad_int2str(filterCompare, filter.u.ip.srcPortComp, "??"),
1264                    ntohs(filter.u.ip.srcport));
1265       p += i;
1266       len -= i;
1267     }
1268
1269     if (filter.u.ip.dstPortComp > RAD_NO_COMPARE) {
1270       i = snprintf(p, len, " dstport %s %d",
1271                    lrad_int2str(filterCompare, filter.u.ip.dstPortComp, "??"),
1272                    ntohs(filter.u.ip.dstport));
1273       p += i;
1274       len -= i;
1275     }
1276
1277     if (filter.u.ip.established) {
1278       i = snprintf(p, len, " est");
1279       p += i;
1280       len -= i;
1281     }
1282
1283     /*
1284      *  Handle IPX filters
1285      */
1286   } else if (filter.type == RAD_FILTER_IPX) {
1287     /* print for source */
1288     if (filter.u.ipx.src.net) {
1289       i = snprintf(p, len, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1290                   (unsigned int)ntohl(filter.u.ipx.src.net),
1291                   filter.u.ipx.src.node[0], filter.u.ipx.src.node[1],
1292                   filter.u.ipx.src.node[2], filter.u.ipx.src.node[3],
1293                   filter.u.ipx.src.node[4], filter.u.ipx.src.node[5]);
1294       p += i;
1295       len -= i;
1296
1297       if (filter.u.ipx.srcSocComp > RAD_NO_COMPARE) {
1298         i = snprintf(p, len, " srcipxsock %s 0x%04x",
1299                      lrad_int2str(filterCompare, filter.u.ipx.srcSocComp, "??"),
1300                      ntohs(filter.u.ipx.src.socket));
1301         p += i;
1302         len -= i;
1303       }
1304     }
1305
1306     /* same for destination */
1307     if (filter.u.ipx.dst.net) {
1308       i = snprintf(p, len, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1309                   (unsigned int)ntohl(filter.u.ipx.dst.net),
1310                   filter.u.ipx.dst.node[0], filter.u.ipx.dst.node[1],
1311                   filter.u.ipx.dst.node[2], filter.u.ipx.dst.node[3],
1312                   filter.u.ipx.dst.node[4], filter.u.ipx.dst.node[5]);
1313       p += i;
1314       len -= i;
1315
1316       if (filter.u.ipx.dstSocComp > RAD_NO_COMPARE) {
1317         i = snprintf(p, len, " dstipxsock %s 0x%04x",
1318                      lrad_int2str(filterCompare, filter.u.ipx.dstSocComp, "??"),
1319                      ntohs(filter.u.ipx.dst.socket));
1320         p += i;
1321         len -= i;
1322       }
1323     }
1324
1325
1326   } else if (filter.type == RAD_FILTER_GENERIC) {
1327     int count;
1328
1329     i = snprintf(p, len, " %u ", (unsigned int) ntohs(filter.u.generic.offset));
1330     p += i;
1331     i -= len;
1332
1333     /* show the mask */
1334     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1335       i = snprintf(p, len, "%02x", filter.u.generic.mask[count]);
1336       p += i;
1337       len -= i;
1338     }
1339
1340     strcpy(p, " ");
1341     p++;
1342     len--;
1343
1344     /* show the value */
1345     for (count = 0; count < ntohs(filter.u.generic.len); count++) {
1346       i = snprintf(p, len, "%02x", filter.u.generic.value[count]);
1347       p += i;
1348       len -= i;
1349     }
1350
1351     i = snprintf(p, len, " %s", (filter.u.generic.compNeq) ? "!=" : "==");
1352     p += i;
1353     len -= i;
1354
1355     if (filter.u.generic.more != 0) {
1356       i = snprintf(p, len, " more");
1357       p += i;
1358       len -= i;
1359     }
1360   }
1361
1362   *(p++) = '"';
1363   *p = '\0';
1364 }