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