fd6283f2df818b918e0f78b915971551cfee9757
[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 WITH_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  *      ascend_parse_ipx_net
355  *
356  *      srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
357  */
358 static int ascend_parse_ipx_net(int argc, char **argv,
359                                 ascend_ipx_net_t *net, uint8_t *comp)
360 {
361         int             token;
362         const char      *p;
363
364         if (argc < 3) return -1;
365
366         /*
367          *      Parse the net, which is a hex number.
368          */
369         net->net = htonl(strtol(argv[0], NULL, 16));
370
371         /*
372          *      Parse the node.
373          */
374         token = fr_str2int(filterKeywords, argv[1], -1);
375         switch (token) {
376         case FILTER_IPX_SRC_IPXNODE:
377         case FILTER_IPX_DST_IPXNODE:
378                 break;
379
380         default:
381                 return -1;
382         }
383
384         /*
385          *      Can have a leading "0x" or "0X"
386          */
387         p = argv[2];
388         if ((memcmp(p, "0X", 2) == 0) ||
389             (memcmp(p, "0x", 2) == 0)) p += 2;
390
391         /*
392          *      Node must be 6 octets long.
393          */
394         token = fr_hex2bin(p, net->node, IPX_NODE_ADDR_LEN);
395         if (token != IPX_NODE_ADDR_LEN) return -1;
396
397         /*
398          *      Nothing more, die.
399          */
400         if (argc == 3) return 3;
401
402         /*
403          *      Can't be too little or too much.
404          */
405         if (argc != 6) return -1;
406
407         /*
408          *      Parse the socket.
409          */
410         token = fr_str2int(filterKeywords, argv[3], -1);
411         switch (token) {
412         case FILTER_IPX_SRC_IPXSOCK:
413         case FILTER_IPX_DST_IPXSOCK:
414                 break;
415
416         default:
417                 return -1;
418         }
419
420         /*
421          *      Parse the command "<", ">", "=" or "!="
422          */
423         token = fr_str2int(filterCompare, argv[4], -1);
424         switch (token) {
425         case RAD_COMPARE_LESS:
426         case RAD_COMPARE_EQUAL:
427         case RAD_COMPARE_GREATER:
428         case RAD_COMPARE_NOT_EQUAL:
429                 *comp = token;
430                 break;
431
432         default:
433                 return -1;
434         }
435
436         /*
437          *      Parse the value.
438          */
439         token = strtoul(argv[5], NULL, 16);
440         if (token > 65535) return -1;
441
442         net->socket = token;
443         net->socket = htons(net->socket);
444
445
446         /*
447          *      Everything's OK, we parsed 6 entries.
448          */
449         return 6;
450 }
451
452 /*
453  *      ascend_parse_ipx_filter
454  *
455  *      This routine parses an IPX filter string from a string.
456  *      The format of the string is:
457  *
458  *      [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
459  *      [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
460  *
461  * Fields in [...] are optional.
462  *      where:
463  *
464  *  srcipxnet:      Keyword for source IPX address.
465  *                  nnnn = IPX Node address.
466  *
467  *  srcipxnode:     Keyword for source IPX Node address.
468  *                  mmmmm = IPX Node Address, could be FFFFFF.
469  *                  A vlid ipx node number should accompany ipx net number.
470  *
471  *      srcipxsoc:      Keyword for source IPX socket address.
472  *
473  *      cmd:            One of ">" or "<" or "=" or "!=".
474  *
475  *      value:          Socket value to be compared against, in hex.
476  *
477  *      dstipxnet:      Keyword for destination IPX address.
478  *                      nnnn = IPX Node address.
479  *
480  *      dstipxnode:     Keyword for destination IPX Node address.
481  *              mmmmm = IPX Node Address, could be FFFFFF.
482  *                     A valid ipx node number should accompany ipx net number.
483  *
484  *      dstipxsoc:      Keyword for destination IPX socket address.
485  *
486  *      cmd:            One of ">" or "<" or "=" or "!=".
487  *
488  *      value:          Socket value to be compared against, in hex.
489  */
490 static int ascend_parse_ipx(int argc, char **argv, ascend_ipx_filter_t *filter)
491 {
492         int rcode;
493         int token;
494         int flags = 0;
495
496         /*
497          *      We may have nothing, in which case we simply return.
498          */
499         if (argc == 0) return 0;
500
501         /*
502          *      Must have "net N node M"
503          */
504         if (argc < 4) return -1;
505
506         while ((argc > 0) && (flags != 0x03)) {
507                 token = fr_str2int(filterKeywords, argv[0], -1);
508                 switch (token) {
509                 case FILTER_IPX_SRC_IPXNET:
510                         if (flags & 0x01) return -1;
511                         rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
512                                                      &(filter->src),
513                                                      &(filter->srcSocComp));
514                         if (rcode < 0) return -1;
515                         argc -= (rcode + 1);
516                         argv += rcode + 1;
517                         flags |= 0x01;
518                         break;
519
520                 case FILTER_IPX_DST_IPXNET:
521                         if (flags & 0x02) return -1;
522                         rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
523                                                      &(filter->dst),
524                                                      &(filter->dstSocComp));
525                         if (rcode < 0) return -1;
526                         argc -= (rcode + 1);
527                         argv += rcode + 1;
528                         flags |= 0x02;
529                         break;
530
531                 default:
532                         fr_strerror_printf("Unknown string \"%s\" in IPX data filter",
533                                    argv[0]);
534                         return -1;
535                 }
536         }
537
538         /*
539          *      Arguments left over: die.
540          */
541         if (argc != 0) return -1;
542
543         /*
544          *      Everything's OK.
545          */
546         return 0;
547 }
548
549
550 /*
551  *      Parse an IP address and optionally a netmask, to a uint32_t.
552  *
553  *      ipaddr should already be initialized to zero.
554  *      ipaddr is in network byte order.
555  *
556  *      Returns -1 on error, or the number of bits in the netmask, otherwise.
557  */
558 static int ascend_parse_ipaddr(uint32_t *ipaddr, char *str)
559 {
560         int             count = 0;
561         int             ip[4];
562         int             masklen;
563         uint32_t        netmask = 0;
564
565         /*
566          *      Look for IP's.
567          */
568         count = 0;
569         while (*str && (count < 4) && (netmask == 0)) {
570         next:
571                 ip[count] = 0;
572
573                 while (*str) {
574                         switch (*str) {
575                         case '0': case '1': case '2': case '3':
576                         case '4': case '5': case '6': case '7':
577                         case '8': case '9':
578                                 ip[count] *= 10;
579                                 ip[count] += (*str) - '0';
580                                 str++;
581                                 break;
582
583
584                         case '.': /* dot between IP numbers. */
585                                 str++;
586                                 if (ip[count] > 255) return -1;
587
588                                 /*
589                                  *      24, 16, 8, 0, done.
590                                  */
591                                 *ipaddr |= (ip[count] << (8 * (3 - count)));
592                                 count++;
593                                 goto next;
594
595                         case '/': /* netmask  */
596                                 str++;
597                                 masklen = atoi(str);
598                                 if ((masklen < 0) || (masklen > 32)) return -1;
599                                 str += strspn(str, "0123456789");
600                                 netmask = masklen;
601                                 goto finalize;
602                                 break;
603
604                         default:
605                                 fr_strerror_printf("Invalid character in IP address");
606                                 return -1;
607                         }
608                 } /* loop over one character */
609         } /* loop until the count hits 4 */
610
611         if (count == 3) {
612         finalize:
613                 /*
614                  *      Do the last one, too.
615                  */
616                 if (ip[count] > 255) return -1;
617
618                 /*
619                  *      24, 16, 8, 0, done.
620                  */
621                 *ipaddr |= (ip[count] << (8 * (3 - count)));
622         }
623
624         /*
625          *      We've hit the end of the IP address, and there's something
626          *      else left over: die.
627          */
628         if (*str) return -1;
629
630         /*
631          *      Set the default netmask.
632          */
633         if (!netmask) {
634                 if (!*ipaddr) {
635                         netmask = 0;
636                 } else if ((*ipaddr & 0x80000000) == 0) {
637                         netmask = 8;
638                 } else if ((*ipaddr & 0xc0000000) == 0x80000000) {
639                         netmask = 16;
640                 } else if ((*ipaddr & 0xe0000000) == 0xc0000000) {
641                         netmask = 24;
642                 } else {
643                         netmask = 32;
644                 }
645         }
646
647         *ipaddr = htonl(*ipaddr);
648         return netmask;
649 }
650
651 /*
652  *      ascend_parse_port:  Parse a comparator and port.
653  *
654  *      Returns -1 on error, or the comparator.
655  */
656 static int ascend_parse_port(uint16_t *port, char *compare, char *str)
657 {
658         int rcode, token = -1;
659
660         /*
661          *      There MUST be a comparison string.
662          */
663         rcode = fr_str2int(filterCompare, compare, -1);
664         if (rcode < 0) return rcode;
665
666         if (strspn(str, "0123456789") == strlen(str)) {
667                 token = atoi(str);
668         } else {
669                 token = fr_str2int(filterPortType, str, -1);
670         }
671
672         if ((token < 0) || (token > 65535)) return -1;
673
674         *port = token;
675         *port = htons(*port);
676
677         return rcode;
678 }
679
680
681 #define IP_SRC_ADDR_FLAG    (1 << 0)
682 #define IP_DEST_ADDR_FLAG   (1 << 1)
683 #define IP_SRC_PORT_FLAG    (1 << 2)
684 #define IP_DEST_PORT_FLAG   (1 << 3)
685 #define IP_PROTO_FLAG       (1 << 4)
686 #define IP_EST_FLAG         (1 << 5)
687
688 #define DONE_FLAGS      (IP_SRC_ADDR_FLAG | IP_DEST_ADDR_FLAG | \
689                         IP_SRC_PORT_FLAG | IP_DEST_PORT_FLAG | \
690                         IP_PROTO_FLAG | IP_EST_FLAG)
691
692 /*
693  *      ascend_parse_ip:
694  *
695  *      This routine parses an IP filter string from a RADIUS
696  *      reply. The format of the string is:
697  *
698  *      ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
699  *          [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
700  *
701  *      Fields in [...] are optional.
702  *
703  *      dstip:          Keyword for destination IP address.
704  *                      n.n.n.n = IP address. /nn - netmask.
705  *
706  *      srcip:          Keyword for source IP address.
707  *                      n.n.n.n = IP address. /nn - netmask.
708  *
709  *      proto:          Optional protocol field. Either a name or
710  *                      number. Known names are in FilterProtoName[].
711  *
712  *      dstport:        Keyword for destination port. Only valid with tcp
713  *                      or udp. 'cmp' are in FilterPortType[]. 'value' can be
714  *                      a name or number.
715  *
716  *      srcport:        Keyword for source port. Only valid with tcp
717  *                      or udp. 'cmp' are in FilterPortType[]. 'value' can be
718  *                      a name or number.
719  *
720  *      est:            Keyword for TCP established. Valid only for tcp.
721  *
722  */
723 static int ascend_parse_ip(int argc, char **argv, ascend_ip_filter_t *filter)
724 {
725         int rcode;
726         int token;
727         int flags;
728
729         /*
730          *      We may have nothing, in which case we simply return.
731          */
732         if (argc == 0) return 0;
733
734         /*
735          *      There may, or may not, be src & dst IP's in the string.
736          */
737         flags = 0;
738         while ((argc > 0) && (flags != DONE_FLAGS)) {
739                 token = fr_str2int(filterKeywords, argv[0], -1);
740                 switch (token) {
741                 case FILTER_IP_SRC:
742                         if (flags & IP_SRC_ADDR_FLAG) return -1;
743                         if (argc < 2) return -1;
744
745                         rcode = ascend_parse_ipaddr(&filter->srcip, argv[1]);
746                         if (rcode < 0) return rcode;
747
748                         filter->srcmask = rcode;
749                         flags |= IP_SRC_ADDR_FLAG;
750                         argv += 2;
751                         argc -= 2;
752                         break;
753
754                 case FILTER_IP_DST:
755                         if (flags & IP_DEST_ADDR_FLAG) return -1;
756                         if (argc < 2) return -1;
757
758                         rcode = ascend_parse_ipaddr(&filter->dstip, argv[1]);
759                         if (rcode < 0) return rcode;
760
761                         filter->dstmask = rcode;
762                         flags |= IP_DEST_ADDR_FLAG;
763                         argv += 2;
764                         argc -= 2;
765                         break;
766
767                 case FILTER_IP_SRC_PORT:
768                         if (flags & IP_SRC_PORT_FLAG) return -1;
769                         if (argc < 3) return -1;
770
771                         rcode = ascend_parse_port(&filter->srcport,
772                                                   argv[1], argv[2]);
773                         if (rcode < 0) return rcode;
774                         filter->srcPortComp = rcode;
775
776                         flags |= IP_SRC_PORT_FLAG;
777                         argv += 3;
778                         argc -= 3;
779                         break;
780
781                 case FILTER_IP_DST_PORT:
782                         if (flags & IP_DEST_PORT_FLAG) return -1;
783                         if (argc < 3) return -1;
784
785                         rcode = ascend_parse_port(&filter->dstport,
786                                                   argv[1], argv[2]);
787                         if (rcode < 0) return rcode;
788                         filter->dstPortComp = rcode;
789
790                         flags |= IP_DEST_PORT_FLAG;
791                         argv += 3;
792                         argc -= 3;
793                         break;
794
795                 case FILTER_EST:
796                         if (flags & IP_EST_FLAG) return -1;
797                         filter->established = 1;
798                         argv++;
799                         argc--;
800                         flags |= IP_EST_FLAG;
801                         break;
802
803                 default:
804                         if (flags & IP_PROTO_FLAG) return -1;
805                         if (strspn(argv[0], "0123456789") == strlen(argv[0])) {
806                                 token = atoi(argv[0]);
807                         } else {
808                                 token = fr_str2int(filterProtoName, argv[0], -1);
809                                 if (token == -1) {
810                                         fr_strerror_printf("Unknown IP protocol \"%s\" in IP data filter",
811                                                    argv[0]);
812                                         return -1;
813                                 }
814                         }
815                         filter->proto = token;
816                         flags |= IP_PROTO_FLAG;
817
818                         argv++;
819                         argc--;
820                         break;
821                 }
822         }
823
824         /*
825          *      We should have parsed everything by now.
826          */
827         if (argc != 0) {
828                 fr_strerror_printf("Unknown extra string \"%s\" in IP data filter",
829                            argv[0]);
830                 return -1;
831         }
832
833         return 0;
834 }
835
836
837 /*
838  *      ascend_parse_generic
839  *
840  *      This routine parses a Generic filter string from a RADIUS
841  *      reply. The format of the string is:
842  *
843  *      generic dir action offset mask value [== or != ] [more]
844  *
845  *      Fields in [...] are optional.
846  *
847  *      offset:         A Number. Specifies an offset into a frame
848  *                      to start comparing.
849  *
850  *      mask:           A hexadecimal mask of bits to compare.
851  *
852  *      value:          A value to compare with the masked data.
853  *
854  *      compNeq:        Defines type of comparison. ( "==" or "!=")
855  *                      Default is "==".
856  *
857  *      more:           Optional keyword MORE, to represent the attachment
858  *                      to the next entry.
859  */
860 static int ascend_parse_generic(int argc, char **argv,
861                                 ascend_generic_filter_t *filter)
862 {
863         int rcode;
864         int token;
865         int flags;
866
867         /*
868          *      We may have nothing, in which case we simply return.
869          */
870         if (argc == 0) return 0;
871
872         /*
873          *      We need at least "offset mask value"
874          */
875         if (argc < 3) return -1;
876
877         /*
878          *      No more than optional comparison and "more"
879          */
880         if (argc > 5) return -1;
881
882         /*
883          *      Offset is a uint16_t number.
884          */
885         if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;
886
887         rcode = atoi(argv[0]);
888         if (rcode > 65535) return -1;
889
890         filter->offset = rcode;
891         filter->offset = htons(filter->offset);
892
893         rcode = fr_hex2bin(argv[1], filter->mask, sizeof(filter->mask));
894         if (rcode != sizeof(filter->mask)) return -1;
895
896         token = fr_hex2bin(argv[2], filter->value, sizeof(filter->value));
897         if (token != sizeof(filter->value)) return -1;
898
899         /*
900          *      The mask and value MUST be the same length.
901          */
902         if (rcode != token) return -1;
903
904         filter->len = rcode;
905         filter->len = htons(filter->len);
906
907         /*
908          *      Nothing more.  Exit.
909          */
910         if (argc == 3) return 0;
911
912         argc -= 3;
913         argv += 3;
914         flags = 0;
915
916         while (argc >= 1) {
917                 token = fr_str2int(filterKeywords, argv[0], -1);
918                 switch (token) {
919                 case FILTER_GENERIC_COMPNEQ:
920                         if (flags & 0x01) return -1;
921                         filter->compNeq = TRUE;
922                         flags |= 0x01;
923                         break;
924                 case FILTER_GENERIC_COMPEQ:
925                         if (flags & 0x01) return -1;
926                         filter->compNeq = FALSE;
927                         flags |= 0x01;
928                         break;
929
930                 case FILTER_MORE:
931                         if (flags & 0x02) return -1;
932                         filter->more = htons( 1 );
933                         flags |= 0x02;
934                         break;
935
936                 default:
937                         fr_strerror_printf("Invalid string \"%s\" in generic data filter",
938                                    argv[0]);
939                         return -1;
940                 }
941
942                 argc--;
943                 argv++;
944         }
945
946         return 0;
947 }
948
949
950 /*
951  * filterBinary:
952  *
953  * This routine will call routines to parse entries from an ASCII format
954  * to a binary format recognized by the Ascend boxes.
955  *
956  *      pair:                   Pointer to value_pair to place return.
957  *
958  *      valstr:                 The string to parse
959  *
960  *      return:                 -1 for error or 0.
961  */
962 int
963 ascend_parse_filter(VALUE_PAIR *pair)
964 {
965         int             token, type;
966         int             rcode;
967         int             argc;
968         char            *argv[32];
969         ascend_filter_t filter;
970
971         rcode = -1;
972
973         /*
974          *      Rather than printing specific error messages, we create
975          *      a general one here, which won't be used if the function
976          *      returns OK.
977          */
978         fr_strerror_printf("Text is not in proper format");
979
980         /*
981          *      Tokenize the input string in the VP.
982          *
983          *      Once the filter is *completelty* parsed, then we will
984          *      over-write it with the final binary filter.
985          */
986         argc = str2argv(pair->vp_strvalue, argv, 32);
987         if (argc < 3) return -1;
988
989         /*
990          *      Decide which filter type it is: ip, ipx, or generic
991          */
992         type = fr_str2int(filterType, argv[0], -1);
993         memset(&filter, 0, sizeof(filter));
994
995         /*
996          *      Validate the filter type.
997          */
998         switch (type) {
999         case RAD_FILTER_GENERIC:
1000         case RAD_FILTER_IP:
1001         case RAD_FILTER_IPX:
1002                 filter.type = type;
1003                 break;
1004
1005         default:
1006                 fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
1007                 return -1;
1008                 break;
1009         }
1010
1011         /*
1012          *      Parse direction
1013          */
1014         token = fr_str2int(filterKeywords, argv[1], -1);
1015         switch (token) {
1016         case FILTER_IN:
1017                 filter.direction = 1;
1018                 break;
1019
1020         case FILTER_OUT:
1021                 filter.direction = 0;
1022                 break;
1023
1024         default:
1025                 fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
1026                 return -1;
1027                 break;
1028         }
1029
1030         /*
1031          *      Parse action
1032          */
1033         token = fr_str2int(filterKeywords, argv[2], -1);
1034         switch (token) {
1035         case FILTER_FORWARD:
1036                 filter.forward = 1;
1037                 break;
1038
1039         case FILTER_DROP:
1040                 filter.forward = 0;
1041                 break;
1042
1043         default:
1044                 fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
1045                 return -1;
1046                 break;
1047         }
1048
1049
1050         switch (type) {
1051         case RAD_FILTER_GENERIC:
1052                 rcode = ascend_parse_generic(argc - 3, &argv[3],
1053                                           &filter.u.generic);
1054                 break;
1055
1056         case RAD_FILTER_IP:
1057                 rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
1058                 break;
1059
1060         case RAD_FILTER_IPX:
1061                 rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
1062                 break;
1063
1064         default:                /* should never reach here. */
1065                 break;
1066         }
1067
1068         /*
1069          *      Touch the VP only if everything was OK.
1070          */
1071         if (rcode == 0) {
1072                 pair->length = sizeof(filter);
1073                 memcpy(pair->vp_filter, &filter, sizeof(filter));
1074         }
1075
1076         return rcode;
1077
1078 #if 0
1079     /*
1080      * if 'more' is set then this new entry must exist, be a
1081      * FILTER_GENERIC_TYPE, direction and disposition must match for
1082      * the previous 'more' to be valid. If any should fail then TURN OFF
1083      * previous 'more'
1084      */
1085     if( prevRadPair ) {
1086         filt = ( RadFilter * )prevRadPair->vp_strvalue;
1087         if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
1088            ( prevRadPair->attribute != pair->attribute ) ||
1089            ( filt->indirection != radFil.indirection ) ||
1090            ( filt->forward != radFil.forward ) ) {
1091             gen = &filt->u.generic;
1092             gen->more = FALSE;
1093             fr_strerror_printf("filterBinary:  'more' for previous entry doesn't match: %s.\n",
1094                      valstr);
1095         }
1096     }
1097     prevRadPair = NULL;
1098     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
1099         if( radFil.u.generic.more ) {
1100             prevRadPair = pair;
1101         }
1102     }
1103
1104     if( rc != -1 ) {
1105         memcpy( pair->vp_strvalue, &radFil, pair->length );
1106     }
1107     return(rc);
1108
1109 #endif
1110 }
1111
1112 /*
1113  *      Print an Ascend binary filter attribute to a string,
1114  *      Grrr... Ascend makes the server do this work, instead
1115  *      of doing it on the NAS.
1116  *
1117  *      Note we don't bother checking 'len' after the snprintf's.
1118  *      This function should ONLY be called with a large (~1k) buffer.
1119  */
1120 void print_abinary(const VALUE_PAIR *vp, char *buffer, size_t len, int delimitst)
1121 {
1122   size_t                i;
1123   char                  *p;
1124   ascend_filter_t       *filter;
1125
1126   static const char *action[] = {"drop", "forward"};
1127   static const char *direction[] = {"out", "in"};
1128
1129   p = buffer;
1130
1131   /*
1132    *  Just for paranoia: wrong size filters get printed as octets
1133    */
1134   if (vp->length != sizeof(*filter)) {
1135           strcpy(p, "0x");
1136           p += 2;
1137           len -= 2;
1138           for (i = 0; i < vp->length; i++) {
1139                   snprintf(p, len, "%02x", vp->vp_octets[i]);
1140                   p += 2;
1141                   len -= 2;
1142           }
1143           return;
1144   }
1145
1146   if (delimitst) {
1147         *(p++) = '"';
1148         len -= 3;                       /* account for leading & trailing quotes */
1149   }
1150
1151   filter = (ascend_filter_t *) &(vp->vp_filter);
1152   i = snprintf(p, len, "%s %s %s",
1153                fr_int2str(filterType, filter->type, "??"),
1154                direction[filter->direction & 0x01],
1155                action[filter->forward & 0x01]);
1156
1157   p += i;
1158   len -= i;
1159
1160   /*
1161    *    Handle IP filters
1162    */
1163   if (filter->type == RAD_FILTER_IP) {
1164
1165     if (filter->u.ip.srcip) {
1166       i = snprintf(p, len, " srcip %d.%d.%d.%d/%d",
1167                    ((uint8_t *) &filter->u.ip.srcip)[0],
1168                    ((uint8_t *) &filter->u.ip.srcip)[1],
1169                    ((uint8_t *) &filter->u.ip.srcip)[2],
1170                    ((uint8_t *) &filter->u.ip.srcip)[3],
1171                    filter->u.ip.srcmask);
1172       p += i;
1173       len -= i;
1174     }
1175
1176     if (filter->u.ip.dstip) {
1177       i = snprintf(p, len, " dstip %d.%d.%d.%d/%d",
1178                    ((uint8_t *) &filter->u.ip.dstip)[0],
1179                    ((uint8_t *) &filter->u.ip.dstip)[1],
1180                    ((uint8_t *) &filter->u.ip.dstip)[2],
1181                    ((uint8_t *) &filter->u.ip.dstip)[3],
1182                    filter->u.ip.dstmask);
1183       p += i;
1184       len -= i;
1185     }
1186
1187     i =  snprintf(p, len, " %s",
1188                   fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
1189     p += i;
1190     len -= i;
1191
1192     if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
1193       i = snprintf(p, len, " srcport %s %d",
1194                    fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
1195                    ntohs(filter->u.ip.srcport));
1196       p += i;
1197       len -= i;
1198     }
1199
1200     if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
1201       i = snprintf(p, len, " dstport %s %d",
1202                    fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
1203                    ntohs(filter->u.ip.dstport));
1204       p += i;
1205       len -= i;
1206     }
1207
1208     if (filter->u.ip.established) {
1209       i = snprintf(p, len, " est");
1210       p += i;
1211       len -= i;
1212     }
1213
1214     /*
1215      *  Handle IPX filters
1216      */
1217   } else if (filter->type == RAD_FILTER_IPX) {
1218     /* print for source */
1219     if (filter->u.ipx.src.net) {
1220       i = snprintf(p, len, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
1221                   (unsigned int)ntohl(filter->u.ipx.src.net),
1222                   filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
1223                   filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
1224                   filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
1225       p += i;
1226       len -= i;
1227
1228       if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
1229         i = snprintf(p, len, " srcipxsock %s 0x%04x",
1230                      fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
1231                      ntohs(filter->u.ipx.src.socket));
1232         p += i;
1233         len -= i;
1234       }
1235     }
1236
1237     /* same for destination */
1238     if (filter->u.ipx.dst.net) {
1239       i = snprintf(p, len, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
1240                   (unsigned int)ntohl(filter->u.ipx.dst.net),
1241                   filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
1242                   filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
1243                   filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
1244       p += i;
1245       len -= i;
1246
1247       if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
1248         i = snprintf(p, len, " dstipxsock %s 0x%04x",
1249                      fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
1250                      ntohs(filter->u.ipx.dst.socket));
1251         p += i;
1252         len -= i;
1253       }
1254     }
1255
1256
1257   } else if (filter->type == RAD_FILTER_GENERIC) {
1258     int count;
1259
1260     i = snprintf(p, len, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
1261     p += i;
1262     i -= len;
1263
1264     /* show the mask */
1265     for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1266       i = snprintf(p, len, "%02x", filter->u.generic.mask[count]);
1267       p += i;
1268       len -= i;
1269     }
1270
1271     strcpy(p, " ");
1272     p++;
1273     len--;
1274
1275     /* show the value */
1276     for (count = 0; count < ntohs(filter->u.generic.len); count++) {
1277       i = snprintf(p, len, "%02x", filter->u.generic.value[count]);
1278       p += i;
1279       len -= i;
1280     }
1281
1282     i = snprintf(p, len, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
1283     p += i;
1284     len -= i;
1285
1286     if (filter->u.generic.more != 0) {
1287       i = snprintf(p, len, " more");
1288       p += i;
1289       len -= i;
1290     }
1291   }
1292
1293   if (delimitst) *(p++) = '"';
1294   *p = '\0';
1295 }
1296 #endif