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