summaryrefslogtreecommitdiffstats
path: root/src/lib/filters.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/filters.c')
-rw-r--r--src/lib/filters.c1253
1 files changed, 1253 insertions, 0 deletions
diff --git a/src/lib/filters.c b/src/lib/filters.c
new file mode 100644
index 0000000..3f3b63d
--- /dev/null
+++ b/src/lib/filters.c
@@ -0,0 +1,1253 @@
+/*
+ * filters.c Routines to parse Ascend's filter attributes.
+ *
+ * Version: $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2003,2006 The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#ifdef WITH_ASCEND_BINARY
+#include <ctype.h>
+
+/*
+ * Two types of filters are supported, GENERIC and IP. The identifiers
+ * are:
+ */
+
+#define RAD_FILTER_GENERIC 0
+#define RAD_FILTER_IP 1
+#define RAD_FILTER_IPX 2
+
+/*
+ * Generic filters mask and match up to RAD_MAX_FILTER_LEN bytes
+ * starting at some offset. The length is:
+ */
+#define RAD_MAX_FILTER_LEN 6
+
+/*
+ * ASCEND extensions for ABINARY filters
+ */
+
+#define IPX_NODE_ADDR_LEN 6
+
+#if ! defined( false )
+# define false 0
+# define true (! false)
+#endif
+
+
+/*
+ * ascend_ip_filter_t
+ *
+ * The binary format of an IP filter. ALL fields are stored in
+ * network byte order.
+ *
+ * srcip: The source IP address.
+ *
+ * dstip: The destination IP address.
+ *
+ * srcmask: The number of leading one bits in the source address
+ * mask. Specifies the bits of interest.
+ *
+ * dstmask: The number of leading one bits in the destination
+ * address mask. Specifies the bits of interest.
+ *
+ * proto: The IP protocol number
+ *
+ * established: A boolean value. true when we care about the
+ * established state of a TCP connection. false when
+ * we dont care.
+ *
+ * srcport: TCP or UDP source port number.
+ *
+ * dstport: TCP or UDP destination port number.
+ *
+ * srcPortCmp: One of the values of the RadFilterComparison
+ * enumeration, specifying how to compare the
+ * srcport value.
+ *
+ * dstPortCmp: One of the values of the RadFilterComparison
+ * enumeration, specifying how to compare the
+ * dstport value.
+ *
+ * fill: Round things out to a int16_t boundary.
+ */
+typedef struct ascend_ip_filter_t {
+ uint32_t srcip;
+ uint32_t dstip;
+ uint8_t srcmask;
+ uint8_t dstmask;
+ uint8_t proto;
+ uint8_t established;
+ uint16_t srcport;
+ uint16_t dstport;
+ uint8_t srcPortComp;
+ uint8_t dstPortComp;
+ unsigned char fill[4]; /* used to be fill[2] */
+} ascend_ip_filter_t;
+
+
+/*
+ * ascend_ipx_net_t
+ *
+ * net: IPX Net address
+ *
+ * node: IPX Node address
+ *
+ * socket: IPX socket address
+ */
+typedef struct ascend_ipx_net_t {
+ uint32_t net;
+ uint8_t node[IPX_NODE_ADDR_LEN];
+ uint16_t socket;
+} ascend_ipx_net_t;
+
+/*
+ * ascend_ipx_filter_t
+ *
+ * The binary format of an IPX filter. ALL fields are stored in
+ * network byte order.
+ *
+ * src: Source net, node, and socket.
+ *
+ * dst: Destination net, node, and socket.
+ *
+ * srcSocComp: Source socket compare value
+ *
+ * dstSocComp: Destination socket compare value
+ */
+typedef struct ascend_ipx_filter_t {
+ ascend_ipx_net_t src;
+ ascend_ipx_net_t dst;
+ uint8_t srcSocComp;
+ uint8_t dstSocComp;
+} ascend_ipx_filter_t;
+
+
+/*
+ * ascend_generic_filter_t
+ *
+ * The binary format of a GENERIC filter. ALL fields are stored in
+ * network byte order.
+ *
+ * offset: Number of bytes into packet to start comparison.
+ *
+ * len: Number of bytes to mask and compare. May not
+ * exceed RAD_MAX_FILTER_LEN.
+ *
+ * more: Boolean. If non-zero the next filter entry is
+ * also to be applied to a packet.
+ *
+ * mask: A bit mask specifying the bits to compare.
+ *
+ * value: A value to compare against the masked bits at
+ * offset in a users packet.
+ *
+ * compNeq: Defines type of comarison (Equal or Notequal)
+ * default is Equal.
+ *
+ * fill: Round things out to a dword boundary
+ */
+typedef struct ascend_generic_filter_t {
+ uint16_t offset;
+ uint16_t len;
+ uint16_t more;
+ uint8_t mask[ RAD_MAX_FILTER_LEN ];
+ uint8_t value[ RAD_MAX_FILTER_LEN ];
+ uint8_t compNeq;
+ uint8_t fill[3]; /* used to be fill[1] */
+} ascend_generic_filter_t;
+
+/*
+ * ascend_filter_t
+ *
+ * A binary filter element. Contains one of ascend_ip_filter_t,
+ * ascend_ipx_filter_t, or ascend_generic_filter_t.
+ *
+ * All fields are stored in network byte order.
+ *
+ * type: Either RAD_FILTER_GENERIC or RAD_FILTER_IP.
+ *
+ * forward: true if we should forward packets that match this
+ * filter, false if we should drop packets that match
+ * this filter.
+ *
+ * direction: true if this is an input filter, false if this is
+ * an output filter.
+ *
+ * fill: Round things out to a dword boundary.
+ *
+ * u: A union of
+ * ip: An ip filter entry
+ * generic: A generic filter entry
+ */
+typedef struct ascend_filter_t {
+ uint8_t type;
+ uint8_t forward;
+ uint8_t direction;
+ uint8_t fill;
+ union {
+ ascend_ip_filter_t ip;
+ ascend_ipx_filter_t ipx;
+ ascend_generic_filter_t generic;
+ uint8_t data[28]; /* ensure it's 32 bytes */
+ } u;
+} ascend_filter_t;
+
+/*
+ * This is a wild C hack...
+ */
+typedef struct _cpp_hack {
+ char data[(sizeof(ascend_filter_t) == 32) ? 1 : -1 ];
+} _cpp_hack;
+
+/*
+ * FilterPortType:
+ *
+ * Ascii names of some well known tcp/udp services.
+ * Used for filtering on a port type.
+ *
+ * ??? What the heck is wrong with getservbyname?
+ */
+static const FR_NAME_NUMBER filterPortType[] = {
+ { "ftp-data", 20 },
+ { "ftp", 21 },
+ { "telnet", 23 },
+ { "smtp", 25 },
+ { "nameserver", 42 },
+ { "domain", 53 },
+ { "tftp", 69 },
+ { "gopher", 70 },
+ { "finger", 79 },
+ { "www", 80 },
+ { "kerberos", 88 },
+ { "hostname", 101 },
+ { "nntp", 119 },
+ { "ntp", 123 },
+ { "exec", 512 },
+ { "login", 513 },
+ { "cmd", 514 },
+ { "talk", 517 },
+ { NULL , 0},
+};
+
+static const FR_NAME_NUMBER filterType[] = {
+ { "generic", RAD_FILTER_GENERIC},
+ { "ip", RAD_FILTER_IP},
+ { "ipx", RAD_FILTER_IPX},
+ { NULL, 0},
+};
+
+typedef enum {
+ FILTER_GENERIC_TYPE,
+ FILTER_IP_TYPE,
+ FILTER_IN,
+ FILTER_OUT,
+ FILTER_FORWARD,
+ FILTER_DROP,
+ FILTER_GENERIC_OFFSET,
+ FILTER_GENERIC_MASK,
+ FILTER_GENERIC_VALUE,
+ FILTER_GENERIC_COMPNEQ,
+ FILTER_GENERIC_COMPEQ,
+ FILTER_MORE,
+ FILTER_IP_DST,
+ FILTER_IP_SRC,
+ FILTER_IP_PROTO,
+ FILTER_IP_DST_PORT,
+ FILTER_IP_SRC_PORT,
+ FILTER_EST,
+ FILTER_IPX_TYPE,
+ FILTER_IPX_DST_IPXNET,
+ FILTER_IPX_DST_IPXNODE,
+ FILTER_IPX_DST_IPXSOCK,
+ FILTER_IPX_SRC_IPXNET,
+ FILTER_IPX_SRC_IPXNODE,
+ FILTER_IPX_SRC_IPXSOCK
+} FilterTokens;
+
+
+static const FR_NAME_NUMBER filterKeywords[] = {
+ { "ip", FILTER_IP_TYPE },
+ { "generic", FILTER_GENERIC_TYPE },
+ { "in", FILTER_IN },
+ { "out", FILTER_OUT },
+ { "forward", FILTER_FORWARD },
+ { "drop", FILTER_DROP },
+ { "dstip", FILTER_IP_DST },
+ { "srcip", FILTER_IP_SRC },
+ { "dstport", FILTER_IP_DST_PORT },
+ { "srcport", FILTER_IP_SRC_PORT },
+ { "est", FILTER_EST },
+ { "more", FILTER_MORE },
+ { "!=", FILTER_GENERIC_COMPNEQ },
+ { "==", FILTER_GENERIC_COMPEQ },
+ { "ipx", FILTER_IPX_TYPE },
+ { "dstipxnet", FILTER_IPX_DST_IPXNET },
+ { "dstipxnode", FILTER_IPX_DST_IPXNODE },
+ { "dstipxsock", FILTER_IPX_DST_IPXSOCK },
+ { "srcipxnet", FILTER_IPX_SRC_IPXNET },
+ { "srcipxnode", FILTER_IPX_SRC_IPXNODE },
+ { "srcipxsock", FILTER_IPX_SRC_IPXSOCK },
+ { NULL , -1},
+};
+
+/*
+ * FilterProtoName:
+ *
+ * Ascii name of protocols used for filtering.
+ *
+ * ??? What the heck is wrong with getprotobyname?
+ */
+static const FR_NAME_NUMBER filterProtoName[] = {
+ { "tcp", 6 },
+ { "udp", 17 },
+ { "ospf", 89 },
+ { "icmp", 1 },
+ { "0", 0 },
+ { NULL , -1 },
+};
+
+
+/*
+ * RadFilterComparison:
+ *
+ * An enumerated values for the IP filter port comparisons.
+ */
+typedef enum {
+ RAD_NO_COMPARE = 0,
+ RAD_COMPARE_LESS,
+ RAD_COMPARE_EQUAL,
+ RAD_COMPARE_GREATER,
+ RAD_COMPARE_NOT_EQUAL
+} RadFilterComparison;
+
+static const FR_NAME_NUMBER filterCompare[] = {
+ { "<", RAD_COMPARE_LESS },
+ { "=", RAD_COMPARE_EQUAL },
+ { ">", RAD_COMPARE_GREATER },
+ { "!=", RAD_COMPARE_NOT_EQUAL },
+ { NULL, 0 },
+};
+
+
+/*
+ * ascend_parse_ipx_net
+ *
+ * srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
+ */
+static int ascend_parse_ipx_net(int argc, char **argv,
+ ascend_ipx_net_t *net, uint8_t *comp)
+{
+ int token;
+ char const *p;
+
+ if (argc < 3) return -1;
+
+ /*
+ * Parse the net, which is a hex number.
+ */
+ net->net = htonl(strtol(argv[0], NULL, 16));
+
+ /*
+ * Parse the node.
+ */
+ token = fr_str2int(filterKeywords, argv[1], -1);
+ switch (token) {
+ case FILTER_IPX_SRC_IPXNODE:
+ case FILTER_IPX_DST_IPXNODE:
+ break;
+
+ default:
+ return -1;
+ }
+
+ /*
+ * Can have a leading "0x" or "0X"
+ */
+ p = argv[2];
+ if ((memcmp(p, "0X", 2) == 0) ||
+ (memcmp(p, "0x", 2) == 0)) p += 2;
+
+ /*
+ * Node must be 6 octets long.
+ */
+ token = fr_hex2bin(net->node, IPX_NODE_ADDR_LEN, p, strlen(p));
+ if (token != IPX_NODE_ADDR_LEN) return -1;
+
+ /*
+ * Nothing more, die.
+ */
+ if (argc == 3) return 3;
+
+ /*
+ * Can't be too little or too much.
+ */
+ if (argc != 6) return -1;
+
+ /*
+ * Parse the socket.
+ */
+ token = fr_str2int(filterKeywords, argv[3], -1);
+ switch (token) {
+ case FILTER_IPX_SRC_IPXSOCK:
+ case FILTER_IPX_DST_IPXSOCK:
+ break;
+
+ default:
+ return -1;
+ }
+
+ /*
+ * Parse the command "<", ">", "=" or "!="
+ */
+ token = fr_str2int(filterCompare, argv[4], -1);
+ switch (token) {
+ case RAD_COMPARE_LESS:
+ case RAD_COMPARE_EQUAL:
+ case RAD_COMPARE_GREATER:
+ case RAD_COMPARE_NOT_EQUAL:
+ *comp = token;
+ break;
+
+ default:
+ return -1;
+ }
+
+ /*
+ * Parse the value.
+ */
+ token = strtoul(argv[5], NULL, 16);
+ if (token > 65535) return -1;
+
+ net->socket = token;
+ net->socket = htons(net->socket);
+
+
+ /*
+ * Everything's OK, we parsed 6 entries.
+ */
+ return 6;
+}
+
+/*
+ * ascend_parse_ipx_filter
+ *
+ * This routine parses an IPX filter string from a string.
+ * The format of the string is:
+ *
+ * [ srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]]
+ * [ dstipxnet nnnn dstipxnode mmmmm [dstipxsoc cmd value ]]
+ *
+ * Fields in [...] are optional.
+ * where:
+ *
+ * srcipxnet: Keyword for source IPX address.
+ * nnnn = IPX Node address.
+ *
+ * srcipxnode: Keyword for source IPX Node address.
+ * mmmmm = IPX Node Address, could be FFFFFF.
+ * A vlid ipx node number should accompany ipx net number.
+ *
+ * srcipxsoc: Keyword for source IPX socket address.
+ *
+ * cmd: One of ">" or "<" or "=" or "!=".
+ *
+ * value: Socket value to be compared against, in hex.
+ *
+ * dstipxnet: Keyword for destination IPX address.
+ * nnnn = IPX Node address.
+ *
+ * dstipxnode: Keyword for destination IPX Node address.
+ * mmmmm = IPX Node Address, could be FFFFFF.
+ * A valid ipx node number should accompany ipx net number.
+ *
+ * dstipxsoc: Keyword for destination IPX socket address.
+ *
+ * cmd: One of ">" or "<" or "=" or "!=".
+ *
+ * value: Socket value to be compared against, in hex.
+ */
+static int ascend_parse_ipx(int argc, char **argv, ascend_ipx_filter_t *filter)
+{
+ int rcode;
+ int token;
+ int flags = 0;
+
+ /*
+ * We may have nothing, in which case we simply return.
+ */
+ if (argc == 0) return 0;
+
+ /*
+ * Must have "net N node M"
+ */
+ if (argc < 4) return -1;
+
+ while ((argc > 0) && (flags != 0x03)) {
+ token = fr_str2int(filterKeywords, argv[0], -1);
+ switch (token) {
+ case FILTER_IPX_SRC_IPXNET:
+ if (flags & 0x01) return -1;
+ rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
+ &(filter->src),
+ &(filter->srcSocComp));
+ if (rcode < 0) return -1;
+ argc -= (rcode + 1);
+ argv += rcode + 1;
+ flags |= 0x01;
+ break;
+
+ case FILTER_IPX_DST_IPXNET:
+ if (flags & 0x02) return -1;
+ rcode = ascend_parse_ipx_net(argc - 1, argv + 1,
+ &(filter->dst),
+ &(filter->dstSocComp));
+ if (rcode < 0) return -1;
+ argc -= (rcode + 1);
+ argv += rcode + 1;
+ flags |= 0x02;
+ break;
+
+ default:
+ fr_strerror_printf("Unknown string \"%s\" in IPX data filter",
+ argv[0]);
+ return -1;
+ }
+ }
+
+ /*
+ * Arguments left over: die.
+ */
+ if (argc != 0) return -1;
+
+ /*
+ * Everything's OK.
+ */
+ return 0;
+}
+
+
+/*
+ * Parse an IP address and optionally a netmask, to a uint32_t.
+ *
+ * ipaddr should already be initialized to zero.
+ * ipaddr is in network byte order.
+ *
+ * Returns -1 on error, or the number of bits in the netmask, otherwise.
+ */
+static int ascend_parse_ipaddr(uint32_t *ipaddr, char *str)
+{
+ int count = 0;
+ int ip[4];
+ int masklen;
+ uint32_t netmask = 0;
+
+ /*
+ * Look for IP's.
+ */
+ count = 0;
+ while (*str && (count < 4) && (netmask == 0)) {
+ next:
+ ip[count] = 0;
+
+ while (*str) {
+ switch (*str) {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ ip[count] *= 10;
+ ip[count] += (*str) - '0';
+ str++;
+ break;
+
+
+ case '.': /* dot between IP numbers. */
+ str++;
+ if (ip[count] > 255) return -1;
+
+ /*
+ * 24, 16, 8, 0, done.
+ */
+ *ipaddr |= (ip[count] << (8 * (3 - count)));
+ count++;
+ goto next;
+
+ case '/': /* netmask */
+ str++;
+ masklen = atoi(str);
+ if ((masklen < 0) || (masklen > 32)) return -1;
+ str += strspn(str, "0123456789");
+ netmask = masklen;
+ goto finalize;
+
+ default:
+ fr_strerror_printf("Invalid character in IP address");
+ return -1;
+ }
+ } /* loop over one character */
+ } /* loop until the count hits 4 */
+
+ if (count == 3) {
+ finalize:
+ /*
+ * Do the last one, too.
+ */
+ if (ip[count] > 255) return -1;
+
+ /*
+ * 24, 16, 8, 0, done.
+ */
+ *ipaddr |= (ip[count] << (8 * (3 - count)));
+ }
+
+ /*
+ * We've hit the end of the IP address, and there's something
+ * else left over: die.
+ */
+ if (*str) return -1;
+
+ /*
+ * Set the default netmask.
+ */
+ if (!netmask) {
+ if (!*ipaddr) {
+ netmask = 0;
+ } else if ((*ipaddr & 0x80000000) == 0) {
+ netmask = 8;
+ } else if ((*ipaddr & 0xc0000000) == 0x80000000) {
+ netmask = 16;
+ } else if ((*ipaddr & 0xe0000000) == 0xc0000000) {
+ netmask = 24;
+ } else {
+ netmask = 32;
+ }
+ }
+
+ *ipaddr = htonl(*ipaddr);
+ return netmask;
+}
+
+/*
+ * ascend_parse_port: Parse a comparator and port.
+ *
+ * Returns -1 on error, or the comparator.
+ */
+static int ascend_parse_port(uint16_t *port, char *compare, char *str)
+{
+ int rcode, token = -1;
+
+ /*
+ * There MUST be a comparison string.
+ */
+ rcode = fr_str2int(filterCompare, compare, -1);
+ if (rcode < 0) return rcode;
+
+ if (strspn(str, "0123456789") == strlen(str)) {
+ token = atoi(str);
+ } else {
+ token = fr_str2int(filterPortType, str, -1);
+ }
+
+ if ((token < 0) || (token > 65535)) return -1;
+
+ *port = token;
+ *port = htons(*port);
+
+ return rcode;
+}
+
+
+#define IP_SRC_ADDR_FLAG (1 << 0)
+#define IP_DEST_ADDR_FLAG (1 << 1)
+#define IP_SRC_PORT_FLAG (1 << 2)
+#define IP_DEST_PORT_FLAG (1 << 3)
+#define IP_PROTO_FLAG (1 << 4)
+#define IP_EST_FLAG (1 << 5)
+
+#define DONE_FLAGS (IP_SRC_ADDR_FLAG | IP_DEST_ADDR_FLAG | \
+ IP_SRC_PORT_FLAG | IP_DEST_PORT_FLAG | \
+ IP_PROTO_FLAG | IP_EST_FLAG)
+
+/*
+ * ascend_parse_ip:
+ *
+ * This routine parses an IP filter string from a RADIUS
+ * reply. The format of the string is:
+ *
+ * ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
+ * [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
+ *
+ * Fields in [...] are optional.
+ *
+ * dstip: Keyword for destination IP address.
+ * n.n.n.n = IP address. /nn - netmask.
+ *
+ * srcip: Keyword for source IP address.
+ * n.n.n.n = IP address. /nn - netmask.
+ *
+ * proto: Optional protocol field. Either a name or
+ * number. Known names are in FilterProtoName[].
+ *
+ * dstport: Keyword for destination port. Only valid with tcp
+ * or udp. 'cmp' are in FilterPortType[]. 'value' can be
+ * a name or number.
+ *
+ * srcport: Keyword for source port. Only valid with tcp
+ * or udp. 'cmp' are in FilterPortType[]. 'value' can be
+ * a name or number.
+ *
+ * est: Keyword for TCP established. Valid only for tcp.
+ *
+ */
+static int ascend_parse_ip(int argc, char **argv, ascend_ip_filter_t *filter)
+{
+ int rcode;
+ int token;
+ int flags;
+
+ /*
+ * We may have nothing, in which case we simply return.
+ */
+ if (argc == 0) return 0;
+
+ /*
+ * There may, or may not, be src & dst IP's in the string.
+ */
+ flags = 0;
+ while ((argc > 0) && (flags != DONE_FLAGS)) {
+ token = fr_str2int(filterKeywords, argv[0], -1);
+ switch (token) {
+ case FILTER_IP_SRC:
+ if (flags & IP_SRC_ADDR_FLAG) return -1;
+ if (argc < 2) return -1;
+
+ rcode = ascend_parse_ipaddr(&filter->srcip, argv[1]);
+ if (rcode < 0) return rcode;
+
+ filter->srcmask = rcode;
+ flags |= IP_SRC_ADDR_FLAG;
+ argv += 2;
+ argc -= 2;
+ break;
+
+ case FILTER_IP_DST:
+ if (flags & IP_DEST_ADDR_FLAG) return -1;
+ if (argc < 2) return -1;
+
+ rcode = ascend_parse_ipaddr(&filter->dstip, argv[1]);
+ if (rcode < 0) return rcode;
+
+ filter->dstmask = rcode;
+ flags |= IP_DEST_ADDR_FLAG;
+ argv += 2;
+ argc -= 2;
+ break;
+
+ case FILTER_IP_SRC_PORT:
+ if (flags & IP_SRC_PORT_FLAG) return -1;
+ if (argc < 3) return -1;
+
+ rcode = ascend_parse_port(&filter->srcport,
+ argv[1], argv[2]);
+ if (rcode < 0) return rcode;
+ filter->srcPortComp = rcode;
+
+ flags |= IP_SRC_PORT_FLAG;
+ argv += 3;
+ argc -= 3;
+ break;
+
+ case FILTER_IP_DST_PORT:
+ if (flags & IP_DEST_PORT_FLAG) return -1;
+ if (argc < 3) return -1;
+
+ rcode = ascend_parse_port(&filter->dstport,
+ argv[1], argv[2]);
+ if (rcode < 0) return rcode;
+ filter->dstPortComp = rcode;
+
+ flags |= IP_DEST_PORT_FLAG;
+ argv += 3;
+ argc -= 3;
+ break;
+
+ case FILTER_EST:
+ if (flags & IP_EST_FLAG) return -1;
+ filter->established = 1;
+ argv++;
+ argc--;
+ flags |= IP_EST_FLAG;
+ break;
+
+ default:
+ if (flags & IP_PROTO_FLAG) return -1;
+ if (strspn(argv[0], "0123456789") == strlen(argv[0])) {
+ token = atoi(argv[0]);
+ } else {
+ token = fr_str2int(filterProtoName, argv[0], -1);
+ if (token == -1) {
+ fr_strerror_printf("Unknown IP protocol \"%s\" in IP data filter",
+ argv[0]);
+ return -1;
+ }
+ }
+ filter->proto = token;
+ flags |= IP_PROTO_FLAG;
+
+ argv++;
+ argc--;
+ break;
+ }
+ }
+
+ /*
+ * We should have parsed everything by now.
+ */
+ if (argc != 0) {
+ fr_strerror_printf("Unknown extra string \"%s\" in IP data filter",
+ argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * ascend_parse_generic
+ *
+ * This routine parses a Generic filter string from a RADIUS
+ * reply. The format of the string is:
+ *
+ * generic dir action offset mask value [== or != ] [more]
+ *
+ * Fields in [...] are optional.
+ *
+ * offset: A Number. Specifies an offset into a frame
+ * to start comparing.
+ *
+ * mask: A hexadecimal mask of bits to compare.
+ *
+ * value: A value to compare with the masked data.
+ *
+ * compNeq: Defines type of comparison. ( "==" or "!=")
+ * Default is "==".
+ *
+ * more: Optional keyword MORE, to represent the attachment
+ * to the next entry.
+ */
+static int ascend_parse_generic(int argc, char **argv,
+ ascend_generic_filter_t *filter)
+{
+ int rcode;
+ int token;
+ int flags;
+
+ /*
+ * We may have nothing, in which case we simply return.
+ */
+ if (argc == 0) return 0;
+
+ /*
+ * We need at least "offset mask value"
+ */
+ if (argc < 3) return -1;
+
+ /*
+ * No more than optional comparison and "more"
+ */
+ if (argc > 5) return -1;
+
+ /*
+ * Offset is a uint16_t number.
+ */
+ if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;
+
+ rcode = atoi(argv[0]);
+ if (rcode > 65535) return -1;
+
+ filter->offset = rcode;
+ filter->offset = htons(filter->offset);
+
+ rcode = fr_hex2bin(filter->mask, sizeof(filter->mask), argv[1], strlen(argv[1]));
+ if (rcode != sizeof(filter->mask)) return -1;
+
+ token = fr_hex2bin(filter->value, sizeof(filter->value), argv[2], strlen(argv[2]));
+ if (token != sizeof(filter->value)) return -1;
+
+ filter->len = rcode;
+ filter->len = htons(filter->len);
+
+ /*
+ * Nothing more. Exit.
+ */
+ if (argc == 3) return 0;
+
+ argc -= 3;
+ argv += 3;
+ flags = 0;
+
+ while (argc >= 1) {
+ token = fr_str2int(filterKeywords, argv[0], -1);
+ switch (token) {
+ case FILTER_GENERIC_COMPNEQ:
+ if (flags & 0x01) return -1;
+ filter->compNeq = true;
+ flags |= 0x01;
+ break;
+ case FILTER_GENERIC_COMPEQ:
+ if (flags & 0x01) return -1;
+ filter->compNeq = false;
+ flags |= 0x01;
+ break;
+
+ case FILTER_MORE:
+ if (flags & 0x02) return -1;
+ filter->more = htons( 1 );
+ flags |= 0x02;
+ break;
+
+ default:
+ fr_strerror_printf("Invalid string \"%s\" in generic data filter",
+ argv[0]);
+ return -1;
+ }
+
+ argc--;
+ argv++;
+ }
+
+ return 0;
+}
+
+
+/** Filter binary
+ *
+ * This routine will call routines to parse entries from an ASCII format
+ * to a binary format recognized by the Ascend boxes.
+ *
+ * @param out Where to write parsed filter.
+ * @param value ascend filter text.
+ * @param len of value.
+ * @return -1 for error or 0.
+ */
+int ascend_parse_filter(value_data_t *out, char const *value, size_t len)
+{
+ int token, type;
+ int rcode;
+ int argc;
+ char *argv[32];
+ ascend_filter_t filter;
+ char *p;
+
+ rcode = -1;
+
+ /*
+ * Tokenize the input string in the VP.
+ *
+ * Once the filter is *completely* parsed, then we will
+ * over-write it with the final binary filter.
+ */
+ p = talloc_bstrndup(NULL, value, len);
+
+ /*
+ * Rather than printing specific error messages, we create
+ * a general one here, which won't be used if the function
+ * returns OK.
+ */
+ fr_strerror_printf("Failed parsing \"%s\" as ascend filer", p);
+
+ argc = str2argv(p, argv, 32);
+ if (argc < 3) {
+ talloc_free(p);
+ return -1;
+ }
+
+ /*
+ * Decide which filter type it is: ip, ipx, or generic
+ */
+ type = fr_str2int(filterType, argv[0], -1);
+ memset(&filter, 0, sizeof(filter));
+
+ /*
+ * Validate the filter type.
+ */
+ switch (type) {
+ case RAD_FILTER_GENERIC:
+ case RAD_FILTER_IP:
+ case RAD_FILTER_IPX:
+ filter.type = type;
+ break;
+
+ default:
+ fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
+ talloc_free(p);
+ return -1;
+ }
+
+ /*
+ * Parse direction
+ */
+ token = fr_str2int(filterKeywords, argv[1], -1);
+ switch (token) {
+ case FILTER_IN:
+ filter.direction = 1;
+ break;
+
+ case FILTER_OUT:
+ filter.direction = 0;
+ break;
+
+ default:
+ fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
+ talloc_free(p);
+ return -1;
+ }
+
+ /*
+ * Parse action
+ */
+ token = fr_str2int(filterKeywords, argv[2], -1);
+ switch (token) {
+ case FILTER_FORWARD:
+ filter.forward = 1;
+ break;
+
+ case FILTER_DROP:
+ filter.forward = 0;
+ break;
+
+ default:
+ fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
+ talloc_free(p);
+ return -1;
+ }
+
+
+ switch (type) {
+ case RAD_FILTER_GENERIC:
+ rcode = ascend_parse_generic(argc - 3, &argv[3], &filter.u.generic);
+ break;
+
+ case RAD_FILTER_IP:
+ rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
+ break;
+
+ case RAD_FILTER_IPX:
+ rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
+ break;
+ }
+
+ /*
+ * Touch the VP only if everything was OK.
+ */
+ if (rcode == 0) memcpy(out->filter, &filter, sizeof(filter));
+ talloc_free(p);
+
+ return rcode;
+}
+
+/*
+ * Print an Ascend binary filter attribute to a string,
+ * Grrr... Ascend makes the server do this work, instead
+ * of doing it on the NAS.
+ *
+ * Note we don't bother checking 'len' after the snprintf's.
+ * This function should ONLY be called with a large (~1k) buffer.
+ */
+void print_abinary(char *out, size_t outlen, uint8_t const *data, size_t len, int8_t quote)
+{
+ size_t i;
+ char *p;
+ ascend_filter_t const *filter;
+
+ static char const *action[] = {"drop", "forward"};
+ static char const *direction[] = {"out", "in"};
+
+ p = out;
+
+ /*
+ * Just for paranoia: wrong size filters get printed as octets
+ */
+ if (len != sizeof(*filter)) {
+ strcpy(p, "0x");
+ p += 2;
+ outlen -= 2;
+ for (i = 0; i < len; i++) {
+ snprintf(p, outlen, "%02x", data[i]);
+ p += 2;
+ outlen -= 2;
+ }
+ return;
+ }
+
+ if (quote > 0) {
+ *(p++) = (char) quote;
+ outlen -= 3; /* account for leading & trailing quotes */
+ }
+
+ filter = (ascend_filter_t const *) data;
+ i = snprintf(p, outlen, "%s %s %s", fr_int2str(filterType, filter->type, "??"),
+ direction[filter->direction & 0x01], action[filter->forward & 0x01]);
+
+ p += i;
+ outlen -= i;
+
+ /*
+ * Handle IP filters
+ */
+ if (filter->type == RAD_FILTER_IP) {
+
+ if (filter->u.ip.srcip) {
+ i = snprintf(p, outlen, " srcip %d.%d.%d.%d/%d",
+ ((uint8_t const *) &filter->u.ip.srcip)[0],
+ ((uint8_t const *) &filter->u.ip.srcip)[1],
+ ((uint8_t const *) &filter->u.ip.srcip)[2],
+ ((uint8_t const *) &filter->u.ip.srcip)[3],
+ filter->u.ip.srcmask);
+ p += i;
+ outlen -= i;
+ }
+
+ if (filter->u.ip.dstip) {
+ i = snprintf(p, outlen, " dstip %d.%d.%d.%d/%d",
+ ((uint8_t const *) &filter->u.ip.dstip)[0],
+ ((uint8_t const *) &filter->u.ip.dstip)[1],
+ ((uint8_t const *) &filter->u.ip.dstip)[2],
+ ((uint8_t const *) &filter->u.ip.dstip)[3],
+ filter->u.ip.dstmask);
+ p += i;
+ outlen -= i;
+ }
+
+ i = snprintf(p, outlen, " %s", fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
+ p += i;
+ outlen -= i;
+
+ if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
+ i = snprintf(p, outlen, " srcport %s %d",
+ fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
+ ntohs(filter->u.ip.srcport));
+ p += i;
+ outlen -= i;
+ }
+
+ if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
+ i = snprintf(p, outlen, " dstport %s %d",
+ fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
+ ntohs(filter->u.ip.dstport));
+ p += i;
+ outlen -= i;
+ }
+
+ if (filter->u.ip.established) {
+ i = snprintf(p, outlen, " est");
+ p += i;
+ }
+
+ /*
+ * Handle IPX filters
+ */
+ } else if (filter->type == RAD_FILTER_IPX) {
+ /* print for source */
+ if (filter->u.ipx.src.net) {
+ i = snprintf(p, outlen, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
+ (unsigned int)ntohl(filter->u.ipx.src.net),
+ filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
+ filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
+ filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
+ p += i;
+ outlen -= i;
+
+ if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
+ i = snprintf(p, outlen, " srcipxsock %s 0x%04x",
+ fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
+ ntohs(filter->u.ipx.src.socket));
+ p += i;
+ outlen -= i;
+ }
+ }
+
+ /* same for destination */
+ if (filter->u.ipx.dst.net) {
+ i = snprintf(p, outlen, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
+ (unsigned int)ntohl(filter->u.ipx.dst.net),
+ filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
+ filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
+ filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
+ p += i;
+ outlen -= i;
+
+ if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
+ i = snprintf(p, outlen, " dstipxsock %s 0x%04x",
+ fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
+ ntohs(filter->u.ipx.dst.socket));
+ p += i;
+ }
+ }
+ } else if (filter->type == RAD_FILTER_GENERIC) {
+ size_t count, masklen;
+
+ masklen = ntohs(filter->u.generic.len);
+ if (masklen >= sizeof(filter->u.generic.mask)) {
+ *p = '\0';
+ return;
+ }
+
+ i = snprintf(p, outlen, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
+ p += i;
+
+ /* show the mask */
+ for (count = 0; count < masklen; count++) {
+ i = snprintf(p, outlen, "%02x", filter->u.generic.mask[count]);
+ p += i;
+ outlen -= i;
+ }
+
+ strcpy(p, " ");
+ p++;
+ outlen--;
+
+ /* show the value */
+ for (count = 0; count < masklen; count++) {
+ i = snprintf(p, outlen, "%02x", filter->u.generic.value[count]);
+ p += i;
+ outlen -= i;
+ }
+
+ i = snprintf(p, outlen, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
+ p += i;
+ outlen -= i;
+
+ if (filter->u.generic.more != 0) {
+ i = snprintf(p, outlen, " more");
+ p += i;
+ }
+ }
+
+ if (quote > 0) {
+ *(p++) = (char) quote;
+ }
+ *p = '\0';
+}
+
+#endif