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