1 /*
2 * ASCEND: @(#)filters.c 1.3 (95/07/25 00:55:30)
3 *
4 * Copyright (c) 1994 Ascend Communications, Inc.
5 * All rights reserved.
6 *
7 * Permission to copy all or part of this material for any purpose is
8 * granted provided that the above copyright notice and this paragraph
9 * are duplicated in all copies. THIS SOFTWARE IS PROVIDED ``AS IS''
10 * AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
11 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12 * FOR A PARTICULAR PURPOSE.
13 */
14
15 /* $Id: filters.c,v 1.2 1996/12/11 23:17:08 baskar Exp $ */
16
17 /* Taken from Chris Adams' patch for Cistron RADIUS; modified to work
18 standalone -- 2003/05/12, EvB */
19
20 #include <sys/types.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <netinet/in.h>
26 #include <stdlib.h>
27 #include <sys/time.h> /* gettimeofday() */
28
29 #include "ascenddatafilter.h"
30
31 #undef DEBUG
32 #if defined( DEBUG )
33 #define PRINTF( x ) printf x
34 #else
35 #define PRINTF( x )
36 #endif
37
38 #define NO_TOKEN -1
39
40 typedef struct {
41 const char* name;
42 int value;
43 } KeywordStruct;
44
45 /*
46 * FilterPortType:
47 *
48 * Ascii names of some well known tcp/udp services.
49 * Used for filtering on a port type.
50 *
51 */
52
53 static KeywordStruct _filterPortType[] = {
54 { "ftp-data", 20 },
55 { "ftp", 21 },
56 { "telnet", 23 },
57 { "smtp", 25 },
58 { "nameserver", 42 },
59 { "domain", 53 },
60 { "tftp", 69 },
61 { "gopher", 70 },
62 { "finger", 79 },
63 { "www", 80 },
64 { "kerberos", 88 },
65 { "hostname", 101 },
66 { "nntp", 119 },
67 { "ntp", 123 },
68 { "exec", 512 },
69 { "login", 513 },
70 { "cmd", 514 },
71 { "talk", 517 },
72 { NULL , NO_TOKEN },
73 };
74
75 typedef enum {
76 FILTER_IP_TYPE,
77 FILTER_GENERIC_TYPE,
78 FILTER_IN,
79 FILTER_OUT,
80 FILTER_FORWARD,
81 FILTER_DROP,
82 FILTER_GENERIC_OFFSET,
83 FILTER_GENERIC_MASK,
84 FILTER_GENERIC_VALUE,
85 FILTER_GENERIC_COMPNEQ,
86 FILTER_GENERIC_COMPEQ,
87 FILTER_MORE,
88 FILTER_IP_DST,
89 FILTER_IP_SRC,
90 FILTER_IP_PROTO,
91 FILTER_IP_DST_PORT,
92 FILTER_IP_SRC_PORT,
93 FILTER_EST,
94 FILTER_IPX_TYPE,
95 FILTER_IPX_DST_IPXNET,
96 FILTER_IPX_DST_IPXNODE,
97 FILTER_IPX_DST_IPXSOCK,
98 FILTER_IPX_SRC_IPXNET,
99 FILTER_IPX_SRC_IPXNODE,
100 FILTER_IPX_SRC_IPXSOCK
101 } FilterTokens;
102
103
104 static KeywordStruct _filterKeywords[] = {
105 { "ip", FILTER_IP_TYPE },
106 { "generic",FILTER_GENERIC_TYPE },
107 { "in", FILTER_IN },
108 { "out", FILTER_OUT },
109 { "forward",FILTER_FORWARD },
110 { "drop", FILTER_DROP },
111 { "dstip", FILTER_IP_DST },
112 { "srcip", FILTER_IP_SRC },
113 { "dstport",FILTER_IP_DST_PORT },
114 { "srcport",FILTER_IP_SRC_PORT },
115 { "est", FILTER_EST },
116 { "more", FILTER_MORE },
117 { "!=", FILTER_GENERIC_COMPNEQ },
118 { "==", FILTER_GENERIC_COMPEQ },
119 { "ipx", FILTER_IPX_TYPE },
120 { "dstipxnet", FILTER_IPX_DST_IPXNET },
121 { "dstipxnode", FILTER_IPX_DST_IPXNODE },
122 { "dstipxsock", FILTER_IPX_DST_IPXSOCK },
123 { "srcipxnet", FILTER_IPX_SRC_IPXNET },
124 { "srcipxnode", FILTER_IPX_SRC_IPXNODE },
125 { "srcipxsock", FILTER_IPX_SRC_IPXSOCK },
126 { NULL , NO_TOKEN },
127 };
128
129 #define FILTER_DIRECTION 0
130 #define FILTER_DISPOSITION 1
131 #define IP_FILTER_COMPLETE 0x3 /* bits shifted by FILTER_DIRECTION */
132 /* FILTER_DISPOSITION */
133
134 #define IPX_FILTER_COMPLETE 0x3 /* bits shifted by FILTER_DIRECTION */
135 /* FILTER_DISPOSITION */
136
137 #define GENERIC_FILTER_COMPLETE 0x1c3 /* bits shifted for FILTER_DIRECTION */
138 /* FILTER_DISPOSITION, FILTER_GENERIC_OFFSET*/
139 /* FILTER_GENERIC_MASK, FILTER_GENERIC_VALUE*/
140
141 /*
142 * FilterProtoName:
143 *
144 * Ascii name of protocols used for filtering.
145 *
146 */
147 static KeywordStruct _filterProtoName[] = {
148 { "tcp", 6 },
149 { "udp", 17 },
150 { "ospf", 89 },
151 { "icmp", 1 },
152 { NULL , NO_TOKEN },
153 };
154
155 static KeywordStruct _filterCompare[] = {
156 { ">", RAD_COMPARE_GREATER },
157 { "=", RAD_COMPARE_EQUAL },
158 { "<", RAD_COMPARE_LESS },
159 { "!=", RAD_COMPARE_NOT_EQUAL },
160 { NULL , NO_TOKEN },
161 };
162
163 static char _curString[512];
164
165 int _findKey ( char *string, KeywordStruct *list );
166 static int _isAllDigit ( char *token );
167 static short _a2octet ( char *tok, char *retBuf );
168 static char _defaultNetmask ( unsigned long address );
169 static int _ipAddressStringToValue ( char *string, UINT4 *ipAddress,
170 char *netmask);
171 static int _parseIpFilter ( RadFilter *curEntry );
172 static int _parseGenericFilter ( RadFilter *curEntry );
173 static int _parseIpxFilter ( RadFilter *curEntry );
174 static int _stringToNode ( unsigned char* dest, unsigned char* src );
175
176 /*
177 * _findKey:
178 *
179 * Given a table of keywords, it will try and match string to an
180 * entry. If it does it returns that keyword value. if no NO_TOKEN is
181 * returned. A sanity check is made for upper case characters.
182 *
183 * string: Pointer to the token to match.
184 *
185 * list: Point to the list of keywords.
186 *
187 * returns: Keyword value on a match or NO_TOKEN.
188 */
_findKey(char * string,KeywordStruct * list)189 int _findKey(char *string, KeywordStruct *list)
190 {
191 short len;
192 KeywordStruct* entry;
193 char buf[80], *ptr;
194
195 len = strlen( (char *) string );
196 for( ptr = buf ; len; len--, string++ ) {
197 if( isupper( *string ) ) {
198 *ptr++ = tolower( *string );
199 } else {
200 *ptr++ = *string;
201 }
202 }
203 *ptr = 0;
204 entry = list;
205 while( entry->name ) {
206 if( strcmp( entry->name, buf ) == 0 ) {
207 break;
208 }
209 entry++;
210 }
211 return( entry->value );
212 }
213
214 /*
215 * _isAllDigit:
216 *
217 * Routine checks a string to make sure all values are digits.
218 *
219 * token: Pointer to sting to check.
220 *
221 * returns: TRUE if all digits, or FALSE.
222 *
223 */
224
225 static int
_isAllDigit(token)226 _isAllDigit(token)
227 char *token;
228 {
229 int i;
230
231 i = strlen( (char *) token );
232 while( i-- ) {
233 if( isdigit( *token ) ) {
234 token++;
235 } else {
236 break;
237 }
238 }
239 if( i > 0 ) {
240 return( FALSE );
241 }
242
243 return( TRUE );
244 }
245
246 /*
247 * _a2octet:
248 *
249 * Converts the ascii mask and value for generic filters into octets.
250 * It also does a sanity check to see if the string is greater than
251 * MAX_FILTER_LEN. It assumes the sting is hex with NO leading "0x"
252 *
253 * tok: Pointer to the string.
254 *
255 * retBuf: Pointer to place the octets.
256 *
257 * returns: Number of octects or -1 for error.
258 *
259 */
260 static short
_a2octet(tok,retBuf)261 _a2octet(tok, retBuf)
262 char *tok;
263 char *retBuf;
264 {
265 short rc, len, val, retLen, i;
266 char buf[ RAD_MAX_FILTER_LEN *2 ];
267 char *octet = buf;
268
269 rc = -1;
270 retLen = 0;
271
272 if( ( len = strlen( (char*) tok ) ) <= ( RAD_MAX_FILTER_LEN*2 ) ) {
273 retLen = len/2;
274 if( len % 2 ) {
275 retLen++;
276 }
277 memset( buf, '\0', RAD_MAX_FILTER_LEN * 2 );
278 for( ; len; len-- ) {
279 if( *tok <= '9' && *tok >= '0' ) {
280 val = '0';
281 *octet++ = *tok++ - val;
282 } else if( isxdigit( *tok ) ) {
283 if( *tok > 'Z' ) {
284 val = 'a';
285 } else {
286 val = 'A';
287 }
288 *octet++ = ( *tok++ - val ) + 10;
289 } else {
290 break;
291 }
292 }
293 if( !len ) {
294 /* merge the values */
295 for( i = 0; i < RAD_MAX_FILTER_LEN*2; i+=2 ) {
296 *retBuf++ = (buf[i] << 4) | buf[i+1];
297 }
298 }
299 }
300
301 if( len ) {
302 rc = -1;
303 } else {
304 rc = retLen;
305 }
306 return( rc );
307 }
308
309
310
311 /*
312 * _defaultNetMask:
313 *
314 * Given an ip address this routine calculate a default netmask.
315 *
316 * address: Ip address.
317 *
318 * returns: Number of bits for the netmask
319 *
320 */
321 static char
_defaultNetmask(address)322 _defaultNetmask(address)
323 unsigned long address;
324 {
325 char netmask;
326
327 if ( ! address ) {
328 netmask = 0;
329 } else if (( address & htonl( 0x80000000 ) ) == 0 ) {
330 netmask = 8;
331 } else if (( address & htonl( 0xc0000000 ) ) == htonl( 0x80000000 ) ) {
332 netmask = 16;
333 } else if (( address & htonl( 0xe0000000 ) ) == htonl( 0xc0000000 ) ) {
334 netmask = 24;
335 } else {
336 netmask = 32;
337 }
338 return netmask;
339 }
340
341
342 static char ipAddressDigits[] = "1234567890./";
343 /*
344 * This functions attempts to convert an IP address in ASCII dot
345 * with an optional netmask part to a pair of IpAddress. Note:
346 * An IpAddress is always stored in network byte order.
347 *
348 * Parameters:
349 *
350 * string: Pointer to a NULL terminated IP address in dot
351 * notation followed by an optional /nn to indicate
352 * the number leading of bits in the netmask.
353 *
354 * ipAddress: Pointer to an IpAddress where the converted
355 * address will be stored.
356 *
357 * netmask: Pointer to an IpAddress where the netmask
358 * will be stored. If no netmask is passed as
359 * as part of the address the default netmask will
360 * be stored here.
361 *
362 * Returns:
363 * <> TRUE if valid conversion, FALSE otherwise.
364 *
365 * *ipAddress: If function returns TRUE, the IP address in NBO.
366 * *netmask: If function returns TRUE, the netmask in NBO.
367 */
368
369 static int
_ipAddressStringToValue(char * string,UINT4 * ipAddress,char * netmask)370 _ipAddressStringToValue(char *string, UINT4 *ipAddress,
371 char *netmask)
372 {
373 u_char* dst;
374 char* cp;
375 int numDots;
376 int i;
377 long value;
378
379 if ( ! string ) {
380 return(FALSE);
381 }
382
383 /* Allow an IP address to be blanked instead of forcing entry of
384 0.0.0.0 -- the user will like it. */
385
386 if ( *string == 0 ) {
387 *ipAddress = 0;
388 *netmask = 0;
389 return TRUE;
390 }
391
392 /* First just count the number of dots in the address. If there
393 are more or less than three the address is invalid. */
394
395 cp = string;
396 numDots = 0;
397 while( *cp ) {
398 if( !strchr( ipAddressDigits, *cp) ) {
399 return( FALSE );
400 }
401 if ( *cp == '.') {
402 ++numDots;
403 }
404 ++cp;
405 }
406 if ( numDots != 3 ) {
407 return( FALSE );
408 }
409
410 dst = (u_char *) ipAddress;
411 cp = string;
412
413 for ( i = 0; i < sizeof( *ipAddress ); i++ ) {
414 value = strtol( cp, (char**) &cp, 10 );
415 if (( value < 0 ) || ( value > 255 )) {
416 return( FALSE );
417 }
418 *dst++ = (u_char) value;
419 if ( *cp == '.' ) {
420 cp += 1;
421 }
422 }
423
424 /* If there is a netmask part, parse it, otherwise figure out the
425 default netmask for this class of address. */
426
427 if ( *cp == '/' ) {
428 value = strtol( cp + 1, (char**) &cp, 10 );
429 if (( *cp != 0 ) || ( value < 0 ) || ( value > 32 )) {
430 return FALSE;
431 }
432 *netmask = (char) value;
433 } else {
434 *netmask = _defaultNetmask( *ipAddress );
435 }
436 return TRUE;
437 }
438
439 /*
440 * Convert a 12 digit string representation of a hex data field to a
441 * value.
442 */
443 static int
_stringToNode(dest,src)444 _stringToNode(dest, src )
445 unsigned char* dest;
446 unsigned char* src;
447 {
448 int srcIx = 0;
449 int ix;
450 int nibble1;
451 int nibble2;
452 int temp;
453 unsigned char *src1;
454
455 src1 = (unsigned char *) strchr(src, 'x');
456
457 if (src1 == NULL)
458 src1 = (unsigned char *) strchr(src,'X');
459
460 if (src1 == NULL)
461 src1 = src;
462 else
463 src1++;
464
465 /* skip any leading 0x or 0X 's */
466 temp = strlen( (char*) src1 );
467 if( strlen( (unsigned char*) src1 ) != ( IPX_NODE_ADDR_LEN * 2 ) ) {
468 return( FALSE );
469 }
470
471 for ( ix = 0; ix < IPX_NODE_ADDR_LEN; ++ix ) {
472 if ( src1[ srcIx ] <= '9' ) {
473 nibble1 = src1[ srcIx ] & 0x0f;
474 } else {
475 nibble1 = (src1[ srcIx ] & 0x0f) + 9;
476 }
477 srcIx += 1;
478 if ( src1[ srcIx ] <= '9' ) {
479 nibble2 = src1[ srcIx ] & 0x0f;
480 } else {
481 nibble2 = (src1[ srcIx ] & 0x0f) + 9;
482 }
483 srcIx += 1;
484 ((unsigned char *) dest)[ ix ] = (unsigned char) (nibble1 << 4) + nibble2;
485 }
486
487 return( TRUE );
488 }
489
490
491 /*
492 * _parseIpxFilter:
493 *
494 * This routine parses an IPX filter string from a RADIUS
495 * reply. The format of the string is:
496 *
497 * ipx dir action [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
498 * [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
499 *
500 * Fields in [...] are optional.
501 * where:
502 *
503 * ipx: Keyword to designate an IPX filter. Actually this
504 * has been determined by _parseFilter.
505 *
506 * dir: Filter direction. "IN" or "OUT"
507 *
508 * action: Filter action. "FORWARD" or "DROP"
509 *
510 * srcipxnet: Keyword for source IPX address.
511 * nnnn = IPX Node address.
512 *
513 * srcipxnode: Keyword for source IPX Node address.
514 * mmmmm = IPX Node Address, could be FFFFFF.
515 * A vlid ipx node number should accompany ipx net number.
516 *
517 * srcipxsoc: Keyword for source IPX socket address.
518 *
519 * cmd: One of ">" or "<" or "=" or "!=".
520 *
521 * value: Socket value to be compared against, in hex.
522 *
523 * dstipxnet: Keyword for destination IPX address.
524 * nnnn = IPX Node address.
525 *
526 * dstipxnode: Keyword for destination IPX Node address.
527 * mmmmm = IPX Node Address, could be FFFFFF.
528 * A vlid ipx node number should accompany ipx net number.
529 *
530 * dstipxsoc: Keyword for destination IPX socket address.
531 *
532 * cmd: One of ">" or "<" or "=" or "!=".
533 *
534 * value: Socket value to be compared against, in hex.
535 *
536 *
537 * expects:
538 *
539 * curEntry: Pointer to place the filter structure
540 *
541 * returns: -1 for error or 0 for OK
542 *
543 */
544
545 static int
_parseIpxFilter(curEntry)546 _parseIpxFilter(curEntry)
547 RadFilter *curEntry;
548 {
549 unsigned long elements = 0l;
550 int tok;
551 char* token;
552 RadIpxFilter* ipx;
553
554 token = (char *) strtok( NULL, " " );
555
556 memset( curEntry, '\0', sizeof( RadFilter ) );
557 curEntry->type = RAD_FILTER_IPX;
558 ipx = &curEntry->u.ipx;
559
560 while( token ) {
561 tok = _findKey( token, _filterKeywords );
562 switch( tok ) {
563 case FILTER_IN:
564 case FILTER_OUT:
565 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
566 PRINTF((" got FILTER %s ", tok == FILTER_IN?"IN":"OUT"));
567 elements |= (1 << FILTER_DIRECTION );
568 break;
569
570 case FILTER_FORWARD:
571 case FILTER_DROP:
572 PRINTF((" got FILTER %s ",
573 tok == FILTER_DROP? "DROP":"FORWARD"));
574
575 elements |= (1 << FILTER_DISPOSITION );
576 if( tok == FILTER_FORWARD ) {
577 curEntry->forward = TRUE;
578 } else {
579 curEntry->forward = FALSE;
580 }
581 break;
582
583 case FILTER_IPX_DST_IPXNET:
584 case FILTER_IPX_SRC_IPXNET:
585 PRINTF((" got FILTER_IPX %s IPXNET ",
586 tok == FILTER_IPX_DST_IPXNET ? "DST":"SRC"));
587 token = (char *) strtok( NULL, " " );
588
589 if ( token ) {
590 if( tok == FILTER_IPX_DST_IPXNET ) {
591 ipx->dstIpxNet = ntohl( strtol( token, 0, 16 ));
592 PRINTF(("D.Net: %08lX token: %s \n", htonl(ipx->dstIpxNet), token));
593 } else {
594 ipx->srcIpxNet = ntohl( strtol( token, 0, 16 ));
595 PRINTF(("S Net: %08lX token: %s \n", htonl(ipx->srcIpxNet), token));
596 }
597 break;
598 }
599 goto doneErr;
600
601 case FILTER_IPX_DST_IPXNODE:
602 case FILTER_IPX_SRC_IPXNODE:
603 PRINTF((" got FILTER_IPX %s IPXNODE ",
604 tok == FILTER_IPX_DST_IPXNODE ? "DST":"SRC"));
605 token = (char *) strtok( NULL, " " );
606
607 if ( token ) {
608 if ( tok == FILTER_IPX_DST_IPXNODE) {
609 _stringToNode( (unsigned char *)ipx->dstIpxNode, (unsigned char*)token );
610 PRINTF(("D. Node: %08lX%04X \n",
611 htonl((*(int *)(ipx->dstIpxNode))),
612 htons((*(short *)(ipx->dstIpxNode+4)))));
613 } else {
614 _stringToNode( (unsigned char *)ipx->srcIpxNode, (unsigned char*)token );
615 PRINTF(("S. Node: %08lX%04X \n",
616 htonl((*(int *)(ipx->srcIpxNode))),
617 htons((*(short *)(ipx->srcIpxNode+4)))));
618 }
619 break;
620 }
621 goto doneErr;
622
623 case FILTER_IPX_DST_IPXSOCK:
624 case FILTER_IPX_SRC_IPXSOCK:
625 {
626 RadFilterComparison cmp;
627
628 PRINTF((" got FILTER_IPX %s IPXSOCK",
629 tok == FILTER_IPX_DST_IPXSOCK ? "DST":"SRC"));
630 token = (char *) strtok( NULL, " " );
631
632 if ( token ) {
633 cmp = _findKey( token, _filterCompare );
634 PRINTF((" cmp value = %d \n", cmp ));
635 if( cmp != NO_TOKEN ) {
636 token = (char *) strtok( NULL, " " );
637 if ( token ) {
638 if ( tok == FILTER_IPX_DST_IPXSOCK ) {
639 ipx->dstSocComp = cmp;
640 ipx->dstIpxSoc =
641 ntohs( (IpxSocket) strtol( token, NULL, 16 ));
642 PRINTF(("%X \n", htons(ipx->dstIpxSoc)));
643 } else {
644 ipx->srcSocComp = cmp;
645 ipx->srcIpxSoc
646 = ntohs( (IpxSocket) strtol( token, NULL, 16 ));
647 PRINTF(("%X \n", htons(ipx->srcIpxSoc)));
648 }
649 break;
650 }
651 }
652 }
653 goto doneErr;
654 }
655
656 default:
657 /* no keyword match */
658 goto doneErr;
659 }
660 token = (char *) strtok( NULL, " " );
661 }
662
663 if( elements == IPX_FILTER_COMPLETE ) {
664 return( 0 );
665 }
666
667 doneErr:
668 PRINTF(( "RADIF: IPX Filter syntax error %s \n", token ));
669 fprintf(stderr, "ipx filter error: do not recognize %s in %s \n",
670 token, _curString );
671 return( -1 );
672 }
673
674 /*
675 * _parseIpFilter:
676 *
677 * This routine parses an IP filter string from a RADIUS
678 * reply. The format of the string is:
679 *
680 * ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
681 * [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
682 *
683 * Fields in [...] are optional.
684 * where:
685 *
686 * ip: Keyword to designate an IP filter. Actually this
687 * has been determined by _parseFilter.
688 *
689 * dir: Filter direction. "IN" or "OUT"
690 *
691 * action: Filter action. "FORWARD" or "DROP"
692 *
693 * dstip: Keyword for destination IP address.
694 * n.n.n.n = IP address. /nn - netmask.
695 *
696 * srcip: Keyword for source IP address.
697 * n.n.n.n = IP address. /nn - netmask.
698 *
699 * proto: Optional protocol field. Either a name or
700 * number. Known names are in FilterProtoName[].
701 *
702 * dstpost: Keyword for destination port. Only valid with tcp
703 * or udp. 'cmp' are in FilterPortType[]. 'value' can be
704 * a name or number.
705 *
706 * srcpost: Keyword for source port. Only valid with tcp
707 * or udp. 'cmp' are in FilterPortType[]. 'value' can be
708 * a name or number.
709 *
710 * est: Keyword for TCP established. Valid only for tcp.
711 *
712 * expects:
713 *
714 * curEntry: Pointer to place the filter structure
715 *
716 * returns: -1 for error or 0 for OK
717 *
718 */
719
720 static int
_parseIpFilter(curEntry)721 _parseIpFilter(curEntry)
722 RadFilter *curEntry;
723 {
724
725 unsigned long elements = 0l;
726 int tok;
727 char* token;
728 RadIpFilter* ip;
729
730 token = (char *) strtok( NULL, " " );
731
732 PRINTF((" in ip filter \n"));
733
734 memset( curEntry, '\0', sizeof( RadFilter ) );
735 curEntry->type = RAD_FILTER_IP;
736 ip = &curEntry->u.ip;
737 ip->established = FALSE;
738
739 while( token ) {
740 PRINTF((" token %s ", token ));
741 tok = _findKey( token, _filterKeywords );
742 switch( tok ) {
743 case FILTER_IN:
744 case FILTER_OUT:
745 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
746 PRINTF((" got %s ", tok == FILTER_IN?"FILTER_IN":"FILTER_OUT"));
747 elements |= (1 << FILTER_DIRECTION );
748 break;
749 case FILTER_FORWARD:
750 case FILTER_DROP:
751 PRINTF((" got %s ", tok == FILTER_DROP?
752 "FILTER_DROP":"FILTER_FORWARD"));
753 elements |= (1 << FILTER_DISPOSITION );
754 if( tok == FILTER_FORWARD ) {
755 curEntry->forward = TRUE;
756 } else {
757 curEntry->forward = FALSE;
758 }
759 break;
760 case FILTER_IP_DST:
761 case FILTER_IP_SRC:
762 PRINTF((" got %s ", tok == FILTER_IP_DST?
763 "FILTER_IP_DST":"FILTER_IP_SRC"));
764 token = (char *) strtok( NULL, " " );
765 if ( token ) {
766 if( tok == FILTER_IP_DST ) {
767
768 if( _ipAddressStringToValue( (char*)token,
769 &ip->dstip, (char *)&ip->dstmask ) ) {
770 PRINTF((" ip %lx netmask %lx \n", ip->dstip,
771 ip->dstmask ));
772 break;
773 }
774 } else {
775 if( _ipAddressStringToValue( (char *)token,
776 &ip->srcip, (char *)&ip->srcmask ) ) {
777 PRINTF((" ip %lx netmask %lx \n", ip->srcip,
778 ip->srcmask ));
779 break;
780 }
781 }
782 }
783
784 PRINTF(( "RADIF: IP Filter syntax error %s \n", token ));
785 fprintf(stderr, "ip filter error: do not recognize %s in %s \n",
786 token, _curString );
787 goto doneErr ;
788
789 case FILTER_IP_DST_PORT:
790 case FILTER_IP_SRC_PORT:
791 {
792 RadFilterComparison cmp;
793 short port;
794
795 PRINTF((" got %s ", tok == FILTER_IP_DST_PORT?
796 "FILTER_IP_DST_PORT":"FILTER_IP_SRC_PORT"));
797 token = (char *) strtok( NULL, " " );
798 if ( token ) {
799 cmp = _findKey( token, _filterCompare );
800 PRINTF((" cmp value = %d \n", cmp ));
801 if( cmp != NO_TOKEN ) {
802 token = (char *) strtok( NULL, " " );
803 if ( token ) {
804 if( _isAllDigit( token ) ) {
805 port = atoi( (char *) token );
806 } else {
807 port = _findKey( token, _filterPortType );
808 }
809 if( port != (short) NO_TOKEN ) {
810 PRINTF((" port = %d \n", port ));
811 if( tok == FILTER_IP_DST_PORT ) {
812 ip->dstPortComp = cmp;
813 ip->dstport = htons( port );
814 } else {
815 ip->srcPortComp = cmp;
816 ip->srcport = htons( port );
817 }
818 break;
819 }
820 }
821 }
822 }
823 fprintf(stderr, "ip filter error: do not recognize %s in %s \n",
824 token, _curString );
825 PRINTF(( "RADIF: IP Filter syntax error %s \n", token ));
826 goto doneErr;
827 break;
828 }
829 case FILTER_EST:
830 PRINTF((" got est %s ", token ));
831 ip->established = TRUE;
832 break;
833 default:
834 /* no keyword match but may match a protocol list */
835 if( _isAllDigit( token ) ) {
836 tok = atoi( (char *) token );
837 } else {
838 tok = _findKey( token, _filterProtoName );
839
840 if( tok == NO_TOKEN ) {
841 PRINTF(( "RADIF: IP proto error %s \n", token ));
842 fprintf(stderr, "ip filter error: do not recognize %s in %s \n",
843 token, _curString );
844 goto doneErr;
845 }
846 }
847 ip->proto = tok;
848 PRINTF(("ip proto cmd = %d ", tok));
849 }
850 token = (char *) strtok( NULL, " " );
851 }
852
853 if( elements == IP_FILTER_COMPLETE ) {
854 return( 0 );
855 }
856
857 doneErr:
858 PRINTF((" done err \n"));
859 return( -1 );
860 }
861
862 /*
863 * _parseGenericFilter:
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 * where:
872 *
873 * generic: Keyword to indicate a generic filter. This
874 * has been determined by _parseFilter.
875 *
876 * dir: Filter direction. "IN" or "OUT"
877 *
878 * action: Filter action. "FORWARD" or "DROP"
879 *
880 * offset: A Number. Specifies an offset into a frame
881 * to start comparing.
882 *
883 * mask: A hexadecimal mask of bits to compare.
884 *
885 * value: A value to compare with the masked data.
886 *
887 * compNeq: Defines type of comparison. ( "==" or "!=")
888 * Default is "==".
889 *
890 * more: Optional keyword MORE, to represent the attachment
891 * to the next entry.
892 *
893 * expects:
894 *
895 * curEntry: Pointer to place the filter structure
896 *
897 * returns: -1 for error or 0 for OK
898 *
899 */
900
901 static int
_parseGenericFilter(curEntry)902 _parseGenericFilter(curEntry)
903 RadFilter *curEntry;
904 {
905 unsigned long elements = 0l;
906 int tok;
907 int gstate = FILTER_GENERIC_OFFSET;
908 char* token;
909 short valLen, maskLen;
910 RadGenericFilter* gen;
911
912 token = (char *) strtok( NULL, " " );
913
914 PRINTF((" in parse generic filter \n"));
915
916 maskLen = 0;
917 memset( (char *)curEntry, '\0', sizeof( RadFilter ) );
918 curEntry->type = RAD_FILTER_GENERIC;
919 gen = &curEntry->u.generic;
920 gen->more = FALSE;
921 gen->compNeq = FALSE;
922
923 while( token ) {
924 PRINTF((" token %s ", token ));
925 tok = _findKey( token, _filterKeywords );
926 PRINTF(("tok %d ", tok));
927 switch( tok ) {
928 case FILTER_IN:
929 case FILTER_OUT:
930 curEntry->indirection = tok == FILTER_IN ? TRUE: FALSE;
931 elements |= (1 << FILTER_DIRECTION );
932 PRINTF((" got %s ", tok == FILTER_IN?"FILTER_IN":"FILTER_OUT"));
933 break;
934 case FILTER_FORWARD:
935 case FILTER_DROP:
936 elements |= (1 << FILTER_DISPOSITION );
937 PRINTF((" got %s ", tok == FILTER_DROP?
938 "FILTER_DROP":"FILTER_FORWARD"));
939 if( tok == FILTER_FORWARD ) {
940 curEntry->forward = TRUE;
941 } else {
942 curEntry->forward = FALSE;
943 }
944 break;
945 case FILTER_GENERIC_COMPNEQ:
946 gen->compNeq = TRUE;
947 PRINTF((" got compare %s ", token));
948 break;
949 case FILTER_GENERIC_COMPEQ:
950 gen->compNeq = FALSE;
951 PRINTF((" got compare %s ", token));
952 break;
953 case FILTER_MORE:
954 gen->more = htons( TRUE );
955 PRINTF((" got more %s ", token ));
956 break;
957 default:
958 elements |= ( 1 << gstate );
959 switch( gstate ) {
960 case FILTER_GENERIC_OFFSET:
961 gstate = FILTER_GENERIC_MASK;
962 gen->offset = htons( atoi( (char *) token ) );
963 break;
964 case FILTER_GENERIC_MASK:
965 gstate = FILTER_GENERIC_VALUE;
966 maskLen = _a2octet( token, (char *)gen->mask );
967 if( maskLen == (short) -1 ) {
968 fprintf(stderr, "filter mask error: %s \n", _curString );
969 goto doneErr;
970 }
971 #ifdef DEBUG
972 PRINTF((" octet retlen = %d ", maskLen ));
973 for( tok = 0; tok < maskLen; tok++) {
974 PRINTF(("%2x", gen->mask[tok]));
975 }
976 PRINTF(("\n"));
977 #endif
978
979 break;
980 case FILTER_GENERIC_VALUE:
981 gstate ++;
982 valLen = _a2octet( token, (char *)gen->value );
983 if( valLen != maskLen ) {
984 fprintf(stderr, "filter value size is not the same size as the filter mask: %s \n",
985 _curString );
986 goto doneErr;
987 }
988 gen->len = htons( valLen );
989 #ifdef DEBUG
990 PRINTF((" octet retlen = %d ", maskLen ));
991 for( tok = 0; tok < maskLen; tok++) {
992 PRINTF(("%2x", gen->value[tok]));
993 }
994 PRINTF(("\n"));
995 #endif
996
997 break;
998 default:
999 fprintf(stderr, "filter: do not know %s in %s \n",
1000 token, _curString );
1001 PRINTF(( "RADIF: Filter syntax error %s \n", token ));
1002 goto doneErr;
1003 }
1004 }
1005 token = (char *) strtok( NULL, " " );
1006 }
1007
1008 if( elements == GENERIC_FILTER_COMPLETE ) {
1009 return( 0 );
1010 }
1011
1012 doneErr:
1013 PRINTF((" done err \n"));
1014 return( -1 );
1015 }
1016
1017 /*
1018 * filterBinary:
1019 *
1020 * This routine will call routines to parse entries from an ASCII format
1021 * to a binary format recognized by the Ascend boxes.
1022 *
1023 * pair: Pointer to value_pair to place return.
1024 *
1025 * return: -1 for error or 0.
1026 */
1027 int
main(int argc,char ** argv)1028 main(int argc, char **argv)
1029 {
1030 char* token;
1031 unsigned long tok;
1032 int rc, n;
1033 RadFilter radFil;
1034
1035 if (argc != 2) {
1036 fprintf(stderr, "Usage: %s \"ascend filter definition\"\n", argv[0]);
1037 return 1;
1038 }
1039
1040 rc = -1;
1041 strncpy( _curString, argv[1], 511); _curString[511] = 0;
1042
1043 token = (char *) strtok(argv[1], " " );
1044 tok = _findKey( token, _filterKeywords );
1045 switch( tok ) {
1046 case FILTER_IP_TYPE:
1047 rc = _parseIpFilter( &radFil );
1048 break;
1049 case FILTER_GENERIC_TYPE:
1050 rc = _parseGenericFilter( &radFil );
1051 break;
1052 case FILTER_IPX_TYPE:
1053 rc = _parseIpxFilter( &radFil );
1054 break;
1055 }
1056
1057 if (rc == -1) {
1058 fprintf(stderr, "Could not parse argument, error code %d.\n", rc);
1059 return rc;
1060 }
1061
1062 printf("Use in the behaviour file: Ascend-Data-Filter = \"");
1063 for(n = 0; n < sizeof(radFil); n++)
1064 {
1065 printf("\\x%02x", ((char *)&radFil)[n] & 0xff);
1066 }
1067 printf("\"\n\n");
1068
1069 printf("Use in MySQL: insert into data (..., attribute, value) values (..., 'Ascend-Data-Filter', 0x");
1070 for(n = 0; n < sizeof(radFil); n++)
1071 {
1072 printf("%02x", ((char *)&radFil)[n] & 0xff);
1073 }
1074 printf(");\n");
1075
1076 return 0;
1077 }
1078
1079