andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
dc8c34
From b791dc9fbd1b88d033d848e3cb0db8f93e2dbff5 Mon Sep 17 00:00:00 2001
dc8c34
From: Mark Reynolds <mareynol@redhat.com>
dc8c34
Date: Wed, 9 May 2012 16:03:59 -0400
dc8c34
Subject: [PATCH 374/375] Ticket #196 - RFE: Interpret IPV6 addresses for ACIs,
dc8c34
 replication, and chaining
dc8c34
dc8c34
Bug Description:  replication, chaining, and access control do not hanbdle IPv6 addresses
dc8c34
dc8c34
Fix Description:  For replication and chaining, we just needed to put brackets "[]" around
dc8c34
                  the IP address in the ldap urls.  We also need to update
dc8c34
                  convert_to_openldap_uri(), which is called by slapi_ldap_init_ext().
dc8c34
dc8c34
                  Access control needed to remove the IPv6 restriction check, and update
dc8c34
                  libaccess to be IPv6 aware.
dc8c34
dc8c34
https://fedorahosted.org/389/ticket/196
dc8c34
dc8c34
Reviewed by: Norkio!
dc8c34
dc8c34
(cherry picked from commit 4d7d59e756d5dc2dbc04c7ee95759f211795a093)
dc8c34
(cherry picked from commit 44413ce12710f310d377727399abc8f9a870c5eb)
dc8c34
---
dc8c34
 ldap/servers/plugins/acl/acllas.c                  |  53 +--
dc8c34
 ldap/servers/plugins/replication/repl5_plugins.c   |   7 +-
dc8c34
 ldap/servers/plugins/replication/windows_private.c |   7 +-
dc8c34
 ldap/servers/slapd/ldaputil.c                      |  90 +++--
dc8c34
 ldap/servers/slapd/slapi-plugin.h                  |   9 +
dc8c34
 lib/libaccess/lasip.cpp                            | 397 ++++++++++++++++-----
dc8c34
 lib/libaccess/lasip.h                              |   3 +-
dc8c34
 7 files changed, 407 insertions(+), 159 deletions(-)
dc8c34
dc8c34
diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c
dc8c34
index fc7f185..a0cc53d 100644
dc8c34
--- a/ldap/servers/plugins/acl/acllas.c
dc8c34
+++ b/ldap/servers/plugins/acl/acllas.c
dc8c34
@@ -281,16 +281,12 @@ int
dc8c34
 DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
dc8c34
  		auth_info, PList_t global_auth, void *arg)
dc8c34
 {
dc8c34
+	struct acl_pblock *aclpb = NULL;
dc8c34
+	PRNetAddr *client_praddr = NULL;
dc8c34
+	char ip_str[256];
dc8c34
+	int rv = LAS_EVAL_TRUE;
dc8c34
 
dc8c34
-	struct acl_pblock	*aclpb = NULL;
dc8c34
-	IPAddr_t        	ip=0;
dc8c34
-	PRNetAddr		client_praddr;
dc8c34
-	struct in_addr		client_addr;
dc8c34
-	int			rv;
dc8c34
-
dc8c34
-
dc8c34
-	rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb,
dc8c34
-				subject, resource, auth_info, global_auth);
dc8c34
+	rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb, subject, resource, auth_info, global_auth);
dc8c34
 	if ( rv != LAS_EVAL_TRUE  || ( NULL == aclpb )) {
dc8c34
 		acl_print_acllib_err(errp, NULL);
dc8c34
 		slapi_log_error( SLAPI_LOG_ACL, plugin_name,
dc8c34
@@ -298,29 +294,34 @@ DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
dc8c34
 		return LAS_EVAL_FAIL;
dc8c34
 	}
dc8c34
 
dc8c34
-	if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR,
dc8c34
-														&client_praddr ) != 0 ) {
dc8c34
-                slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
dc8c34
-                return( LAS_EVAL_FAIL );
dc8c34
-        }
dc8c34
-
dc8c34
-	if ( !PR_IsNetAddrType(&client_praddr, PR_IpAddrV4Mapped) ) {
dc8c34
-	        slapi_log_error( SLAPI_LOG_ACL, plugin_name, 
dc8c34
-				 "Client address is IPv6. ACLs only support IPv4 addresses so far.\n");
dc8c34
+	client_praddr = (PRNetAddr *)slapi_ch_malloc(sizeof(PRNetAddr));
dc8c34
+	if(client_praddr == NULL){
dc8c34
+		slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "DS_LASIpGetter: failed to allocate client_praddr\n");
dc8c34
 		return( LAS_EVAL_FAIL );
dc8c34
 	}
dc8c34
-	        	
dc8c34
-	client_addr.s_addr = client_praddr.ipv6.ip.pr_s6_addr32[3];
dc8c34
 
dc8c34
-	ip = (IPAddr_t) ntohl( client_addr.s_addr );
dc8c34
-	rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)ip, NULL);
dc8c34
+	if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR, client_praddr ) != 0 ) {
dc8c34
+		slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "DS_LASIpGetter: Could not get client IP.\n" );
dc8c34
+		slapi_ch_free((void **)&client_praddr);
dc8c34
+		return( LAS_EVAL_FAIL );
dc8c34
+	}
dc8c34
 	
dc8c34
-	slapi_log_error( SLAPI_LOG_ACL, plugin_name,
dc8c34
-        "Returning client ip address '%s'\n",
dc8c34
-        (slapi_is_loglevel_set(SLAPI_LOG_ACL) ?  inet_ntoa(client_addr) : ""));
dc8c34
+	rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)client_praddr, NULL);
dc8c34
+	if (rv < 0) {
dc8c34
+		slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: "
dc8c34
+			"Couldn't set the client addr property(%d)\n", rv );
dc8c34
+		slapi_ch_free((void **)&client_praddr);
dc8c34
+		return LAS_EVAL_FAIL;
dc8c34
+	}
dc8c34
+	if( PR_NetAddrToString(client_praddr, ip_str, sizeof(ip_str)) == PR_SUCCESS){
dc8c34
+		slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: "
dc8c34
+			"Returning client ip address '%s'\n", ip_str);
dc8c34
+	} else {
dc8c34
+		slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DS_LASIpGetter: "
dc8c34
+			"Returning client ip address 'unknown'\n");
dc8c34
+	}
dc8c34
 
dc8c34
 	return LAS_EVAL_TRUE;
dc8c34
-
dc8c34
 }
dc8c34
 
dc8c34
 /* 
dc8c34
diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c
dc8c34
index afb0364..dddbf15 100644
dc8c34
--- a/ldap/servers/plugins/replication/repl5_plugins.c
dc8c34
+++ b/ldap/servers/plugins/replication/repl5_plugins.c
dc8c34
@@ -133,7 +133,12 @@ multimaster_set_local_purl()
dc8c34
 			}
dc8c34
 			else
dc8c34
 			{
dc8c34
-				local_purl = slapi_ch_smprintf("ldap://%s:%s", host, port);
dc8c34
+				if(slapi_is_ipv6_addr(host)){
dc8c34
+					/* need to put brackets around the ipv6 address */
dc8c34
+					local_purl = slapi_ch_smprintf("ldap://[%s]:%s", host, port);
dc8c34
+				} else {
dc8c34
+					local_purl = slapi_ch_smprintf("ldap://%s:%s", host, port);
dc8c34
+				}
dc8c34
 			}
dc8c34
 
dc8c34
 			/* slapi_ch_free acceptS NULL pointer */
dc8c34
diff --git a/ldap/servers/plugins/replication/windows_private.c b/ldap/servers/plugins/replication/windows_private.c
dc8c34
index a103cad..4381943 100644
dc8c34
--- a/ldap/servers/plugins/replication/windows_private.c
dc8c34
+++ b/ldap/servers/plugins/replication/windows_private.c
dc8c34
@@ -321,7 +321,12 @@ const char* windows_private_get_purl(const Repl_Agmt *ra)
dc8c34
 	char *hostname;
dc8c34
 
dc8c34
 	hostname = agmt_get_hostname(ra);
dc8c34
-	windows_purl = slapi_ch_smprintf("ldap://%s:%d", hostname, agmt_get_port(ra));
dc8c34
+	if(slapi_is_ipv6_addr(hostname)){
dc8c34
+		/* need to put brackets around the ipv6 address */
dc8c34
+		windows_purl = slapi_ch_smprintf("ldap://[%s]:%d", hostname, agmt_get_port(ra));
dc8c34
+	} else {
dc8c34
+		windows_purl = slapi_ch_smprintf("ldap://%s:%d", hostname, agmt_get_port(ra));
dc8c34
+	}
dc8c34
 	slapi_ch_free_string(&hostname);
dc8c34
 
dc8c34
 	return windows_purl;
dc8c34
diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c
dc8c34
index 331dd71..4b8c16d 100644
dc8c34
--- a/ldap/servers/slapd/ldaputil.c
dc8c34
+++ b/ldap/servers/slapd/ldaputil.c
dc8c34
@@ -165,55 +165,59 @@ convert_to_openldap_uri(const char *hostname_or_uri, int port, const char *proto
dc8c34
     char *my_copy = NULL;
dc8c34
     char *start = NULL;
dc8c34
     char *iter = NULL;
dc8c34
+    char *ptr = NULL;
dc8c34
     char *s = NULL;
dc8c34
     const char *brkstr = " ";
dc8c34
+    int done = 0;
dc8c34
 
dc8c34
     if (!hostname_or_uri) {
dc8c34
-	return NULL;
dc8c34
+	    return NULL;
dc8c34
+    }
dc8c34
+
dc8c34
+    if(slapi_is_ipv6_addr(hostname_or_uri)){
dc8c34
+        /* We need to encapsulate the ipv6 addr with brackets  */
dc8c34
+        my_copy = slapi_ch_smprintf("[%s]",hostname_or_uri);
dc8c34
+    } else {
dc8c34
+        my_copy = slapi_ch_strdup(hostname_or_uri);
dc8c34
     }
dc8c34
 
dc8c34
-    my_copy = slapi_ch_strdup(hostname_or_uri);
dc8c34
     /* see if hostname_or_uri is an ldap uri */
dc8c34
     if (!proto && !PL_strncasecmp(my_copy, "ldap", 4)) {
dc8c34
-	start = my_copy + 4;
dc8c34
-	if ((*start == 's') || (*start == 'i')) {
dc8c34
-	    start++;
dc8c34
-	}
dc8c34
-	if (!PL_strncmp(start, "://", 3)) {
dc8c34
-	    *start = '\0';
dc8c34
-	    proto = my_copy;
dc8c34
-	    start += 3;
dc8c34
-	} else {
dc8c34
-	    slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri",
dc8c34
-			    "The given LDAP URI [%s] is not valid\n", hostname_or_uri);
dc8c34
-	    goto end;
dc8c34
-	}
dc8c34
+        start = my_copy + 4;
dc8c34
+        if ((*start == 's') || (*start == 'i')) {
dc8c34
+            start++;
dc8c34
+        }
dc8c34
+        if (!PL_strncmp(start, "://", 3)) {
dc8c34
+            *start = '\0';
dc8c34
+            proto = my_copy;
dc8c34
+            start += 3;
dc8c34
+        } else {
dc8c34
+            slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri",
dc8c34
+                "The given LDAP URI [%s] is not valid\n", hostname_or_uri);
dc8c34
+            goto end;
dc8c34
+        }
dc8c34
     } else if (!proto) {
dc8c34
-	slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri",
dc8c34
-			"The given LDAP URI [%s] is not valid\n", hostname_or_uri);
dc8c34
-	goto end;
dc8c34
+	    slapi_log_error(SLAPI_LOG_FATAL, "convert_to_openldap_uri",
dc8c34
+            "The given LDAP URI [%s] is not valid\n", hostname_or_uri);
dc8c34
+        goto end;
dc8c34
     } else {
dc8c34
-	start = my_copy; /* just assume it's not a uri */
dc8c34
+        start = my_copy; /* just assume it's not a uri */
dc8c34
     }
dc8c34
 	    
dc8c34
-    for (s = ldap_utf8strtok_r(my_copy, brkstr, &iter); s != NULL;
dc8c34
-	 s = ldap_utf8strtok_r(NULL, brkstr, &iter)) {
dc8c34
-	char *ptr;
dc8c34
-	int last = 0;
dc8c34
-	/* strtok will grab the '/' at the end of the uri, if any,
dc8c34
-	   so terminate parsing there */
dc8c34
-	if ((ptr = strchr(s, '/'))) {
dc8c34
-	    *ptr = '\0';
dc8c34
-	    last = 1;
dc8c34
-	}
dc8c34
-	if (retstr) {
dc8c34
-	    retstr = PR_sprintf_append(retstr, "/ %s://%s", proto, s);
dc8c34
-	} else {
dc8c34
-	    retstr = PR_smprintf("%s://%s", proto, s);
dc8c34
-	}
dc8c34
-	if (last) {
dc8c34
-	    break;
dc8c34
-	}
dc8c34
+    for (s = ldap_utf8strtok_r(my_copy, brkstr, &iter); s != NULL; s = ldap_utf8strtok_r(NULL, brkstr, &iter)) {
dc8c34
+        /* strtok will grab the '/' at the end of the uri, if any,  so terminate parsing there */
dc8c34
+        if ((ptr = strchr(s, '/'))) {
dc8c34
+            *ptr = '\0';
dc8c34
+            done = 1;
dc8c34
+        }
dc8c34
+        if (retstr) {
dc8c34
+            retstr = PR_sprintf_append(retstr, "/ %s://%s", proto, s);
dc8c34
+        } else {
dc8c34
+            retstr = PR_smprintf("%s://%s", proto, s);
dc8c34
+        }
dc8c34
+        if (done) {
dc8c34
+            break;
dc8c34
+        }
dc8c34
     }
dc8c34
 
dc8c34
     /* add the port on the last one */
dc8c34
@@ -2271,3 +2275,15 @@ mozldap_ldap_explode_rdn( const char *rdn, const int notypes )
dc8c34
 	return( mozldap_ldap_explode( rdn, notypes, LDAP_RDN ) );
dc8c34
 }
dc8c34
 
dc8c34
+int
dc8c34
+slapi_is_ipv6_addr( const char *hostname ){
dc8c34
+    PRNetAddr addr;
dc8c34
+
dc8c34
+    if(PR_StringToNetAddr(hostname, &addr) == PR_SUCCESS &&
dc8c34
+       !PR_IsNetAddrType(&addr, PR_IpAddrV4Mapped) &&
dc8c34
+       addr.raw.family == PR_AF_INET6)
dc8c34
+    {
dc8c34
+        return 1;
dc8c34
+    }
dc8c34
+    return 0;
dc8c34
+}
dc8c34
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
dc8c34
index 75d5aa9..cb15a25 100644
dc8c34
--- a/ldap/servers/slapd/slapi-plugin.h
dc8c34
+++ b/ldap/servers/slapd/slapi-plugin.h
dc8c34
@@ -3015,6 +3015,15 @@ void slapi_rdn_set_rdn(Slapi_RDN *rdn,const Slapi_RDN *fromrdn);
dc8c34
 void slapi_rdn_free(Slapi_RDN **rdn);
dc8c34
 
dc8c34
 /**
dc8c34
+ * Checks if the value of ipAddress is a IPv6 address
dc8c34
+ *
dc8c34
+ * \param ipAddress is a string that is either an IPv4 or IPv6 address
dc8c34
+ * \return 1 if address is an IPv6 address
dc8c34
+ * \return 0 if address is an IPv4 address
dc8c34
+ */
dc8c34
+int slapi_is_ipv6_addr( const char *ipAddress);
dc8c34
+
dc8c34
+/**
dc8c34
  * Frees and clears the contents of a \c Slapi_RDN structure from memory.
dc8c34
  *
dc8c34
  * Both the RDN value and the array of split RDNs are freed. Those pointers
dc8c34
diff --git a/lib/libaccess/lasip.cpp b/lib/libaccess/lasip.cpp
dc8c34
index 7f5c01e..e66f48a 100644
dc8c34
--- a/lib/libaccess/lasip.cpp
dc8c34
+++ b/lib/libaccess/lasip.cpp
dc8c34
@@ -60,6 +60,8 @@
dc8c34
 #include "aclcache.h"
dc8c34
 #include <libaccess/dbtlibaccess.h>
dc8c34
 #include <libaccess/aclerror.h>
dc8c34
+#include <prio.h>
dc8c34
+#include "nspr.h"
dc8c34
 
dc8c34
 #define        LAS_IP_IS_CONSTANT(x)    (((x) == (LASIpTree_t *)LAS_EVAL_TRUE) || ((x) == (LASIpTree_t *)LAS_EVAL_FALSE))
dc8c34
 
dc8c34
@@ -67,8 +69,9 @@
dc8c34
 extern int LASIpGetIp();
dc8c34
 #endif
dc8c34
 
dc8c34
-static int
dc8c34
-LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop);
dc8c34
+static int colonhex_ipv6(char *ipstr, char *netmaskstr, PRIPv6Addr *ipv6, int *netmask);
dc8c34
+static int LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop);
dc8c34
+static int LASIpAddPatternIPv6(NSErr_t *errp, int netmask, PRIPv6Addr *ipv6, LASIpTree_t **treetop);
dc8c34
 
dc8c34
 /*    dotdecimal
dc8c34
  *    Takes netmask and ip strings and returns the numeric values,
dc8c34
@@ -259,68 +262,108 @@ LASIpTreeDealloc(LASIpTree_t *startnode)
dc8c34
  *    ret code    The usual LAS return codes.
dc8c34
  */
dc8c34
 static int
dc8c34
-LASIpBuild(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, LASIpTree_t **treetop)
dc8c34
+LASIpBuild(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, LASIpContext_t *context)
dc8c34
 {
dc8c34
     unsigned int delimiter;                /* length of valid token     */
dc8c34
     char        token[64], token2[64];    /* a single ip[+netmask]     */
dc8c34
     char        *curptr;                /* current place in attr_pattern */
dc8c34
-    int            netmask, ip;
dc8c34
+    int         netmask = 0;
dc8c34
+    int         ip = 0;
dc8c34
     char        *plusptr;
dc8c34
-    int            retcode;
dc8c34
+    int         retcode;
dc8c34
 
dc8c34
-    if (NULL == treetop) {
dc8c34
+    if (NULL == context) {
dc8c34
         return ACL_RES_ERROR;
dc8c34
     }
dc8c34
 
dc8c34
-    /* ip address can be delimited by space, tab, comma, or carriage return
dc8c34
-     * only.
dc8c34
+    /*
dc8c34
+     *  IP address can be delimited by space, tab, comma, or carriage return only.
dc8c34
      */
dc8c34
     curptr = attr_pattern;
dc8c34
     do {
dc8c34
         delimiter    = strcspn(curptr, ", \t");
dc8c34
         delimiter    = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr);
dc8c34
         strncpy(token, curptr, delimiter);
dc8c34
-		if (delimiter >= sizeof(token)) {
dc8c34
+        if (delimiter >= sizeof(token)) {
dc8c34
             return LAS_EVAL_INVALID;
dc8c34
-		}
dc8c34
+        }
dc8c34
 			
dc8c34
         token[delimiter] = '\0';
dc8c34
         /* skip all the white space after the token */
dc8c34
-        curptr = strpbrk((curptr+delimiter), "1234567890+.*");
dc8c34
-
dc8c34
-        /* Is there a netmask?    */
dc8c34
-        plusptr    = strchr(token, '+');
dc8c34
-        if (plusptr == NULL) {
dc8c34
-            if (curptr && (*curptr == '+')) {
dc8c34
-                /* There was a space before (and possibly after) the plus sign*/
dc8c34
-                curptr = strpbrk((++curptr), "1234567890.*");
dc8c34
-                delimiter    = strcspn(curptr, ", \t");
dc8c34
-                delimiter    = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr);
dc8c34
-				if (delimiter >= sizeof(token2)) {
dc8c34
-					return LAS_EVAL_INVALID;
dc8c34
-				}
dc8c34
-                strncpy(token2, curptr, delimiter);
dc8c34
-                token2[delimiter] = '\0';
dc8c34
-                retcode = dotdecimal(token, token2, &ip, &netmask);
dc8c34
+        curptr = strpbrk((curptr+delimiter), "1234567890+.*ABCDEFabcdef:/");
dc8c34
+
dc8c34
+        /*
dc8c34
+         *  IPv4 addresses do not have ":"
dc8c34
+         */
dc8c34
+        if( strstr(token,":") == NULL ){
dc8c34
+            /* Is there a netmask?    */
dc8c34
+            plusptr = strchr(token, '+');
dc8c34
+            if (plusptr == NULL) {
dc8c34
+                if (curptr && (*curptr == '+')) {
dc8c34
+                    /* There was a space before (and possibly after) the plus sign*/
dc8c34
+                    curptr = strpbrk((++curptr), "1234567890.*");
dc8c34
+                    delimiter = strcspn(curptr, ", \t");
dc8c34
+                    delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr);
dc8c34
+                    if (delimiter >= sizeof(token2)) {
dc8c34
+                        return LAS_EVAL_INVALID;
dc8c34
+                    }
dc8c34
+                    strncpy(token2, curptr, delimiter);
dc8c34
+                    token2[delimiter] = '\0';
dc8c34
+                    retcode = dotdecimal(token, token2, &ip, &netmask);
dc8c34
+                    if (retcode)
dc8c34
+                        return (retcode);
dc8c34
+                    curptr = strpbrk((++curptr), "1234567890+.*");
dc8c34
+                } else {
dc8c34
+                    retcode = dotdecimal(token, "255.255.255.255", &ip, &netmask);
dc8c34
+                    if (retcode)
dc8c34
+                        return (retcode);
dc8c34
+                }
dc8c34
+            } else {
dc8c34
+                /* token is the IP addr string in both cases */
dc8c34
+                *plusptr ='\0';    /* truncate the string */
dc8c34
+                retcode =dotdecimal(token, ++plusptr, &ip, &netmask);
dc8c34
                 if (retcode)
dc8c34
                     return (retcode);
dc8c34
-                curptr = strpbrk((++curptr), "1234567890+.*");
dc8c34
+            }
dc8c34
+
dc8c34
+            if (LASIpAddPattern(errp, netmask, ip, &context->treetop) != 0)
dc8c34
+                return LAS_EVAL_INVALID;
dc8c34
+        } else {
dc8c34
+            /*
dc8c34
+             *  IPv6
dc8c34
+             */
dc8c34
+            PRIPv6Addr ipv6;
dc8c34
+
dc8c34
+            plusptr = strchr(token, '/');
dc8c34
+            if (plusptr == NULL) {
dc8c34
+                if (curptr && (*curptr == '/')) {
dc8c34
+                    /* There was a space before (and possibly after) the plus sign */
dc8c34
+                    curptr = strpbrk((++curptr), "1234567890.*:ABCDEFabcdef");
dc8c34
+                    delimiter = strcspn(curptr, ", \t");
dc8c34
+                    delimiter = (delimiter <= strlen(curptr)) ?	delimiter : strlen(curptr);
dc8c34
+                    strncpy(token2, curptr, delimiter);
dc8c34
+                    token2[delimiter] = '\0';
dc8c34
+                    retcode = colonhex_ipv6(token, token2, &ipv6, &netmask);
dc8c34
+                    if (retcode)
dc8c34
+                        return (retcode);
dc8c34
+                    curptr = strpbrk((++curptr), "1234567890+.:ABCDEFabcdef*");
dc8c34
+                } else {
dc8c34
+                    retcode = colonhex_ipv6(token, "128", &ipv6, &netmask);
dc8c34
+                    if (retcode)
dc8c34
+                        return (retcode);
dc8c34
+                }
dc8c34
             } else {
dc8c34
-                retcode =dotdecimal(token, "255.255.255.255", &ip, &netmask);
dc8c34
+                /* token is the IP addr string in both cases */
dc8c34
+                *plusptr ='\0';    /* truncate the string */
dc8c34
+                retcode = colonhex_ipv6(token, ++plusptr, &ipv6, &netmask);
dc8c34
                 if (retcode)
dc8c34
                     return (retcode);
dc8c34
             }
dc8c34
-        } else {
dc8c34
-            /* token is the IP addr string in both cases */
dc8c34
-            *plusptr ='\0';    /* truncate the string */
dc8c34
-            retcode =dotdecimal(token, ++plusptr, &ip, &netmask);
dc8c34
-            if (retcode)
dc8c34
-                return (retcode);
dc8c34
-        }
dc8c34
-
dc8c34
-        if (LASIpAddPattern(errp, netmask, ip, treetop) != 0)
dc8c34
-            return LAS_EVAL_INVALID;
dc8c34
 
dc8c34
+            if (LASIpAddPatternIPv6(errp, netmask, &ipv6, &context->treetop_ipv6) != (int)NULL) {
dc8c34
+                return LAS_EVAL_INVALID;
dc8c34
+            }
dc8c34
+        }
dc8c34
     } while ((curptr != NULL) && (delimiter != 0));
dc8c34
 
dc8c34
     return 0;
dc8c34
@@ -361,13 +404,15 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop)
dc8c34
     if (*treetop == (LASIpTree_t *)NULL) {    /* No tree at all */
dc8c34
         curptr = LASIpTreeAllocNode(errp);
dc8c34
         if (curptr == NULL) {
dc8c34
-            nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_));
dc8c34
+            nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1,
dc8c34
+                XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_));
dc8c34
             return ACL_RES_ERROR;
dc8c34
         }
dc8c34
         *treetop = curptr;
dc8c34
     }
dc8c34
 
dc8c34
-    /* Special case if the netmask is 0.
dc8c34
+    /*
dc8c34
+     *  Special case if the netmask is 0.
dc8c34
      */
dc8c34
     if (stopbit > 31) {
dc8c34
         (*treetop)->action[0] = (LASIpTree_t *)LAS_EVAL_TRUE;
dc8c34
@@ -375,24 +420,18 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop)
dc8c34
         return 0;
dc8c34
     }
dc8c34
 
dc8c34
-
dc8c34
     /* follow the tree down the pattern path bit by bit until the
dc8c34
      * end of the tree is reached (i.e. a constant).
dc8c34
      */
dc8c34
     for (curbit=31,curptr=*treetop; curbit >= 0; curbit--) {
dc8c34
-
dc8c34
         /* Is the current bit ON?  If so set curval to 1 else 0    */
dc8c34
         curval = (pattern & (1<
dc8c34
 
dc8c34
         /* Are we done, if so remove the rest of the tree     */
dc8c34
         if (curbit == stopbit) {
dc8c34
             LASIpTreeDealloc(curptr->action[curval]);
dc8c34
-            curptr->action[curval] = 
dc8c34
-                    (LASIpTree_t *)LAS_EVAL_TRUE;
dc8c34
-
dc8c34
-            /* This is the normal exit point.  Most other 
dc8c34
-             * exits must be due to errors.
dc8c34
-             */
dc8c34
+            curptr->action[curval] = (LASIpTree_t *)LAS_EVAL_TRUE;
dc8c34
+            /* This is the normal exit point.  Most other  exits must be due to errors. */
dc8c34
             return 0;
dc8c34
         }
dc8c34
 
dc8c34
@@ -401,7 +440,8 @@ LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop)
dc8c34
             newptr = LASIpTreeAllocNode(errp);
dc8c34
             if (newptr == NULL) {
dc8c34
                 LASIpTreeDealloc(*treetop);
dc8c34
-	        nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1));
dc8c34
+                nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1,
dc8c34
+                    XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1));
dc8c34
                 return ACL_RES_ERROR;
dc8c34
             }
dc8c34
             curptr->action[curval] = newptr;
dc8c34
@@ -451,51 +491,57 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
dc8c34
           PList_t subject, PList_t resource, PList_t auth_info,
dc8c34
           PList_t global_auth)
dc8c34
 {
dc8c34
-    int                bit;
dc8c34
-    int                value;
dc8c34
-    IPAddr_t           ip;
dc8c34
-    int                retcode;
dc8c34
-    LASIpTree_t        *node;
dc8c34
-    LASIpContext_t     *context = NULL;
dc8c34
-    int		       rv;
dc8c34
-    char	       ip_str[124];
dc8c34
+    LASIpContext_t *context = NULL;
dc8c34
+    LASIpTree_t *node = NULL;
dc8c34
+    IPAddr_t ip;
dc8c34
+    PRNetAddr *client_addr = NULL;
dc8c34
+    struct in_addr client_inaddr;
dc8c34
+    char ip_str[124];
dc8c34
+    int retcode;
dc8c34
+    int value;
dc8c34
+    int bit;
dc8c34
+    int rc = LAS_EVAL_INVALID;
dc8c34
+    int rv;
dc8c34
 
dc8c34
     *cachable = ACL_INDEF_CACHABLE;
dc8c34
 
dc8c34
     if (strcmp(attr_name, "ip") != 0) {
dc8c34
-	nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2, XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name);
dc8c34
+        nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2,
dc8c34
+            XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name);
dc8c34
         return LAS_EVAL_INVALID;
dc8c34
     }
dc8c34
 
dc8c34
     if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) {
dc8c34
-	nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator));
dc8c34
+        nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2,
dc8c34
+            XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator));
dc8c34
         return LAS_EVAL_INVALID;
dc8c34
     }
dc8c34
 
dc8c34
-    /* GET THE IP ADDR FROM THE SESSION CONTEXT AND STORE IT IN THE
dc8c34
-     * VARIABLE ip.
dc8c34
+    /*
dc8c34
+     *  Get the IP addr from the session context, and store it in "client_addr
dc8c34
      */
dc8c34
 #ifndef    UTEST
dc8c34
-    rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&ip,
dc8c34
-			  subject, resource, auth_info, global_auth);
dc8c34
+    rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&client_addr, subject, resource, auth_info, global_auth);
dc8c34
 
dc8c34
     if (rv != LAS_EVAL_TRUE) {
dc8c34
         if (subject || resource) {
dc8c34
             /* Don't ereport if called from ACL_CachableAclList */
dc8c34
-	    char rv_str[16];
dc8c34
-	    sprintf(rv_str, "%d", rv);
dc8c34
-	    nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str);
dc8c34
+            char rv_str[16];
dc8c34
+            sprintf(rv_str, "%d", rv);
dc8c34
+            nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2,
dc8c34
+                XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str);
dc8c34
         }
dc8c34
-	return LAS_EVAL_FAIL;
dc8c34
+        return LAS_EVAL_FAIL;
dc8c34
     }
dc8c34
 #else
dc8c34
     ip    = (IPAddr_t)LASIpGetIp();
dc8c34
 #endif
dc8c34
 
dc8c34
-    /* If this is the first time through, build the pattern tree first.
dc8c34
+    /*
dc8c34
+     *  If this is the first time through, build the pattern tree first.
dc8c34
      */
dc8c34
     if (*LAS_cookie == NULL) {
dc8c34
-        if (strcspn(attr_pattern, "0123456789.*,+ \t")) {
dc8c34
+        if (strcspn(attr_pattern, "0123456789.*,+ \tABCDEFabcdef:/")) {
dc8c34
             return LAS_EVAL_INVALID;
dc8c34
         }
dc8c34
         ACL_CritEnter();
dc8c34
@@ -503,13 +549,14 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
dc8c34
             *LAS_cookie = context = 
dc8c34
                 (LASIpContext_t *)PERM_MALLOC(sizeof(LASIpContext_t));
dc8c34
             if (context == NULL) {
dc8c34
-                nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1, XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_));
dc8c34
+                nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1,
dc8c34
+                    XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_));
dc8c34
                 ACL_CritExit();
dc8c34
                 return LAS_EVAL_FAIL;
dc8c34
             }
dc8c34
             context->treetop = NULL;
dc8c34
-            retcode = LASIpBuild(errp, attr_name, comparator, attr_pattern, 
dc8c34
-                                 &context->treetop);
dc8c34
+            context->treetop_ipv6 = NULL;
dc8c34
+            retcode = LASIpBuild(errp, attr_name, comparator, attr_pattern, context);
dc8c34
             if (retcode) {
dc8c34
                 ACL_CritExit();
dc8c34
                 return (retcode);
dc8c34
@@ -523,30 +570,194 @@ int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
dc8c34
         context = (LASIpContext *) *LAS_cookie;
dc8c34
         ACL_CritExit();
dc8c34
     }
dc8c34
+    /*
dc8c34
+     *  Check if IP is ipv4/ipv6
dc8c34
+     */
dc8c34
+     if ( PR_IsNetAddrType( client_addr, PR_IpAddrV4Mapped) || client_addr->raw.family == PR_AF_INET ) {
dc8c34
+         /*
dc8c34
+          *  IPv4
dc8c34
+          */
dc8c34
+
dc8c34
+         /* Set the appropriate s_addr for ipv4 or ipv4 mapped to ipv6 */
dc8c34
+         if (client_addr->raw.family == PR_AF_INET) {
dc8c34
+             client_inaddr.s_addr = client_addr->inet.ip;
dc8c34
+         } else {
dc8c34
+             client_inaddr.s_addr = client_addr->ipv6.ip._S6_un._S6_u32[3];
dc8c34
+         }
dc8c34
+
dc8c34
+         node = context->treetop;
dc8c34
+         ip = (IPAddr_t)PR_ntohl( client_inaddr.s_addr );
dc8c34
+
dc8c34
+         if(node == NULL){
dc8c34
+             rc = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
dc8c34
+         } else {
dc8c34
+             for (bit = 31; bit >= 0; bit--) {
dc8c34
+                 value = (ip & (IPAddr_t) (1 << bit)) ? 1 : 0;
dc8c34
+                 if (LAS_IP_IS_CONSTANT(node->action[value])){
dc8c34
+                     /* Reached a result, so return it */
dc8c34
+                     if (comparator == CMP_OP_EQ){
dc8c34
+                         rc = (int)(PRSize)node->action[value];
dc8c34
+                         break;
dc8c34
+                     } else {
dc8c34
+                         rc = ((int)(PRSize)node->action[value] == LAS_EVAL_TRUE) ? LAS_EVAL_FALSE : LAS_EVAL_TRUE;
dc8c34
+                         break;
dc8c34
+                     }
dc8c34
+                 } else {
dc8c34
+                     /* Move on to the next bit */
dc8c34
+                     node = node->action[value];
dc8c34
+                 }
dc8c34
+             }
dc8c34
+         }
dc8c34
+         if(rc == LAS_EVAL_INVALID){
dc8c34
+             sprintf(ip_str, "%x", (unsigned int)ip);
dc8c34
+             nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2,
dc8c34
+                 XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str);
dc8c34
+         }
dc8c34
+    } else {
dc8c34
+        /*
dc8c34
+         *  IPv6
dc8c34
+         */
dc8c34
+        PRIPv6Addr *ipv6 = &(client_addr->ipv6.ip);
dc8c34
+        LASIpTree_t *node;
dc8c34
+        int bit_position = 15;
dc8c34
+        int field = 0;
dc8c34
+        int addr = 0;
dc8c34
+        int value;
dc8c34
+
dc8c34
+        node = context->treetop_ipv6;
dc8c34
+        if ( node == NULL ) {
dc8c34
+            retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
dc8c34
+        } else {
dc8c34
+            addr = PR_ntohs( ipv6->_S6_un._S6_u16[field]);
dc8c34
+            for (bit = 127; bit >= 0 ; bit--, bit_position--) {
dc8c34
+                value = (addr & (1 << bit_position)) ? 1 : 0;
dc8c34
+                if (LAS_IP_IS_CONSTANT(node->action[value])) {
dc8c34
+                    /* Reached a result, so return it */
dc8c34
+                    if (comparator == CMP_OP_EQ){
dc8c34
+                        return(int)(long)node->action[value];
dc8c34
+                    } else {
dc8c34
+                        return(((int)(long)node->action[value] == LAS_EVAL_TRUE) ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
dc8c34
+                    }
dc8c34
+                } else {
dc8c34
+                    node = node->action[value];
dc8c34
+                    if ( bit % 16 == 0) {
dc8c34
+                        /* Ok, move to the next field in the IPv6 addr:  f:f:next:f:f:f:f:f  */
dc8c34
+                        field++;
dc8c34
+                        addr = PR_ntohs( ipv6->_S6_un._S6_u16[field]);
dc8c34
+                        bit_position = 15;
dc8c34
+                    }
dc8c34
+                }
dc8c34
+            }
dc8c34
+            rc = LAS_EVAL_INVALID;
dc8c34
+        }
dc8c34
+    }
dc8c34
+    return rc;
dc8c34
+}
dc8c34
+
dc8c34
+/*
dc8c34
+ *  The ipv6 version of LASIpAddPattern
dc8c34
+ */
dc8c34
+static int
dc8c34
+LASIpAddPatternIPv6(NSErr_t *errp, int netmask, PRIPv6Addr *ipv6, LASIpTree_t **treetop)
dc8c34
+{
dc8c34
+    LASIpTree_t    *curptr;
dc8c34
+    LASIpTree_t    *newptr;
dc8c34
+    int stopbit;
dc8c34
+    int curbit;
dc8c34
+    int curval;
dc8c34
+    int field = 0; /* (8) 16 bit fields in a IPv6 address: x:x:x:x:x:x:x:x */
dc8c34
+    int addr = 0;
dc8c34
+    int curbit_position = 15; /* 16 bits: 0-15 */
dc8c34
+
dc8c34
+    /* stop at the first 1 in the netmask from low to high */
dc8c34
+    stopbit = 128 - netmask;
dc8c34
 
dc8c34
-    node    = context->treetop;
dc8c34
-
dc8c34
-    for (bit=31; bit >=0; bit--) {
dc8c34
-        value    = (ip & (IPAddr_t) (1<
dc8c34
-        if (LAS_IP_IS_CONSTANT(node->action[value])) {
dc8c34
-            /* Reached a result, so return it */
dc8c34
-            if (comparator == CMP_OP_EQ)
dc8c34
-                return((int)(PRSize)node->action[value]);
dc8c34
-            else
dc8c34
-                return(((int)(PRSize)node->action[value] == 
dc8c34
-                    LAS_EVAL_TRUE) ? 
dc8c34
-                    LAS_EVAL_FALSE : LAS_EVAL_TRUE);
dc8c34
-
dc8c34
-        } else
dc8c34
-            /* Move on to the next bit */
dc8c34
-            node = node->action[value];
dc8c34
+    /* Special case if there's no tree.  Allocate the first node    */
dc8c34
+    if (*treetop == (LASIpTree_t *)NULL) {    /* No tree at all */
dc8c34
+        curptr = LASIpTreeAllocNode(errp);
dc8c34
+        if (curptr == NULL) {
dc8c34
+            nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1,
dc8c34
+                XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_));
dc8c34
+            return ACL_RES_ERROR;
dc8c34
+        }
dc8c34
+        *treetop = curptr;
dc8c34
+    }
dc8c34
+
dc8c34
+    addr = PR_ntohs(ipv6->_S6_un._S6_u16[field]);
dc8c34
+    for (curbit = 127, curptr = *treetop; curbit >= 0; curbit--, curbit_position--){
dc8c34
+        /* Is the current bit ON?  If so set curval to 1 else 0 */
dc8c34
+        curval = (addr & (1 << curbit_position)) ? 1 : 0;
dc8c34
+
dc8c34
+        /* Are we done, if so remove the rest of the tree */
dc8c34
+        if (curbit == stopbit) {
dc8c34
+            LASIpTreeDealloc(curptr->action[curval]);
dc8c34
+            curptr->action[curval] = (LASIpTree_t *)LAS_EVAL_TRUE;
dc8c34
+            /* This is the normal exit point.  Most other exits must be due to errors. */
dc8c34
+            return 0;
dc8c34
+        }
dc8c34
+
dc8c34
+        /* Oops reached the end - must allocate  */
dc8c34
+        if (LAS_IP_IS_CONSTANT(curptr->action[curval])) {
dc8c34
+            newptr = LASIpTreeAllocNode(errp);
dc8c34
+            if (newptr == NULL) {
dc8c34
+                LASIpTreeDealloc(*treetop);
dc8c34
+                nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1,
dc8c34
+                    XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1));
dc8c34
+                return ACL_RES_ERROR;
dc8c34
+             }
dc8c34
+             curptr->action[curval] = newptr;
dc8c34
+         }
dc8c34
+
dc8c34
+         /* Keep going down the tree */
dc8c34
+         curptr = curptr->action[curval];
dc8c34
+
dc8c34
+         if ( curbit % 16 == 0) {
dc8c34
+             /* Ok, move to the next field in the addr */
dc8c34
+             field++;
dc8c34
+             addr = PR_ntohs(ipv6->_S6_un._S6_u16[field]);
dc8c34
+             curbit_position = 15;
dc8c34
+         }
dc8c34
     }
dc8c34
+    return ACL_RES_ERROR;
dc8c34
+}
dc8c34
 
dc8c34
-    /* Cannot reach here.  Even a 32 bit mismatch has a conclusion in 
dc8c34
-     * the pattern tree.
dc8c34
+/*
dc8c34
+ *  This is very similar to dotdecimal(), but for ipv6 addresses
dc8c34
+ */
dc8c34
+static int
dc8c34
+colonhex_ipv6(char *ipstr, char *netmaskstr, PRIPv6Addr *ipv6, int *netmask)
dc8c34
+{
dc8c34
+    PRNetAddr addr;
dc8c34
+    /*
dc8c34
+     *  Validate netmaskstr - can only be digits
dc8c34
+     */
dc8c34
+    if (strcspn(netmaskstr, "0123456789")){
dc8c34
+        return LAS_EVAL_INVALID;
dc8c34
+    }
dc8c34
+    /*
dc8c34
+     *  Validate ipstr - can only have digits, colons, hex chars, and dots
dc8c34
+     */
dc8c34
+    if(strcspn(ipstr, "0123456789:ABCDEFabcdef.")){
dc8c34
+        return LAS_EVAL_INVALID;
dc8c34
+    }
dc8c34
+    /*
dc8c34
+     *  validate the netmask - must be between 1 and 128
dc8c34
      */
dc8c34
-    sprintf(ip_str, "%x", (unsigned int)ip);
dc8c34
-    nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str);
dc8c34
-    return LAS_EVAL_INVALID;
dc8c34
+    *netmask = atoi(netmaskstr);
dc8c34
+    if(*netmask < 1 || *netmask > 128){
dc8c34
+        return LAS_EVAL_INVALID;
dc8c34
+    }
dc8c34
+    /*
dc8c34
+     *  Get the net addr
dc8c34
+     */
dc8c34
+    if (PR_StringToNetAddr(ipstr, &addr) != PR_SUCCESS){
dc8c34
+        return LAS_EVAL_INVALID;
dc8c34
+    }
dc8c34
+    /*
dc8c34
+     *  Set the ipv6 addr
dc8c34
+     */
dc8c34
+    *ipv6 = addr.ipv6.ip;
dc8c34
+
dc8c34
+    return 0;
dc8c34
 }
dc8c34
 
dc8c34
diff --git a/lib/libaccess/lasip.h b/lib/libaccess/lasip.h
dc8c34
index c353d9d..c1fe0fc 100644
dc8c34
--- a/lib/libaccess/lasip.h
dc8c34
+++ b/lib/libaccess/lasip.h
dc8c34
@@ -46,5 +46,6 @@ typedef struct LASIpTree {
dc8c34
 } LASIpTree_t;
dc8c34
 
dc8c34
 typedef	struct LASIpContext {
dc8c34
-	LASIpTree_t	*treetop; /* Top of the pattern tree	*/
dc8c34
+	LASIpTree_t *treetop; /* Top of the pattern tree */
dc8c34
+	LASIpTree_t *treetop_ipv6; /* Top of the IPv6 pattern tree */
dc8c34
 } LASIpContext_t;
dc8c34
-- 
dc8c34
2.4.3
dc8c34