[MEDIUM] IPv6 support for stick-tables

Since IPv6 is a different type than IPv4, the pattern fetch functions
src6 and dst6 were added. IPv6 stick-tables can also fetch IPv4 addresses
with src and dst. In this case, the IPv4 addresses are mapped to their
IPv6 counterpart, according to RFC 4291.
diff --git a/include/common/standard.h b/include/common/standard.h
index e20eb77..e7a1052 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -549,4 +549,18 @@
 	return 0;
 }
 
+/* Return true if IPv4 address is part of the network */
+extern int in_net_ipv4(struct in_addr *addr, struct in_addr *mask, struct in_addr *net);
+
+/* Return true if IPv6 address is part of the network */
+extern int in_net_ipv6(struct in6_addr *addr, struct in6_addr *mask, struct in6_addr *net);
+
+/* Map IPv4 adress on IPv6 address, as specified in RFC 3513. */
+extern void v4tov6(struct in6_addr *sin6_addr, struct in_addr *sin_addr);
+
+/* Map IPv6 adress on IPv4 address, as specified in RFC 3513.
+ * Return true if conversion is possible and false otherwise.
+ */
+extern int v6tov4(struct in_addr *sin_addr, struct in6_addr *sin6_addr);
+
 #endif /* _COMMON_STANDARD_H */
diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h
index 26e06df..c7aef6a 100644
--- a/include/proto/proto_tcp.h
+++ b/include/proto/proto_tcp.h
@@ -36,18 +36,19 @@
 int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
 int tcp_exec_req_rules(struct session *s);
 
-/* Converts the TCPv4 source address to a stick_table key usable for table
+/* Converts the TCP source address to a stick_table key usable for table
  * lookups. Returns either NULL if the source cannot be converted (eg: not
  * IPv4) or a pointer to the converted result in static_table_key in the
  * appropriate format (IP).
  */
-static inline struct stktable_key *tcpv4_src_to_stktable_key(struct session *s)
+static inline struct stktable_key *tcp_src_to_stktable_key(struct session *s)
 {
-	/* right now we only support IPv4 */
-	if (s->si[0].addr.c.from.ss_family != AF_INET)
-		return NULL;
-
-	static_table_key.key = (void *)&((struct sockaddr_in *)&s->si[0].addr.c.from)->sin_addr;
+	switch (s->si[0].addr.c.from.ss_family) {
+	case AF_INET:
+		static_table_key.key = (void *)&((struct sockaddr_in *)&s->si[0].addr.c.from)->sin_addr;
+	case AF_INET6:
+		static_table_key.key = (void *)&((struct sockaddr_in6 *)&s->si[0].addr.c.from)->sin6_addr;
+	}
 	return &static_table_key;
 }
 
diff --git a/include/types/pattern.h b/include/types/pattern.h
index a3d5c36..9b4e340 100644
--- a/include/types/pattern.h
+++ b/include/types/pattern.h
@@ -29,6 +29,7 @@
 /* pattern in and out types */
 enum {
 	PATTERN_TYPE_IP = 0,      /* ipv4 type */
+	PATTERN_TYPE_IPV6,        /* ipv6 type */
 	PATTERN_TYPE_INTEGER,     /* unsigned 32bits integer type */
 	PATTERN_TYPE_STRING,      /* char string type */
 	PATTERN_TYPE_DATA,        /* buffer type */
@@ -41,6 +42,7 @@
 /* pattern arg types */
 enum {
 	PATTERN_ARG_TYPE_IP = 0,      /* ipv4 type */
+	PATTERN_ARG_TYPE_IPV6,        /* ipv6 type */
 	PATTERN_ARG_TYPE_INTEGER,     /* unsigned 32bits integer type */
 	PATTERN_ARG_TYPE_SINTEGER,    /* signed 32bits integer type */
 	PATTERN_ARG_TYPE_STRING       /* string type */
@@ -53,6 +55,7 @@
 
 union pattern_arg_data {
 	struct in_addr ip;        /* used for ipv4 type */
+	struct in6_addr ipv6;     /* used for ipv6 type */
 	uint32_t integer;         /* used for unsigned 32bits integer type */
 	int sinteger;             /* used for signed 32bits integer type */
 	struct chunk str;
@@ -66,6 +69,7 @@
 /* pattern result data */
 union pattern_data {
 	struct in_addr ip;        /* used for ipv4 type */
+	struct in6_addr ipv6;     /* used for ipv6 type */
 	uint32_t integer;         /* used for unsigned 32bits integer type */
 	struct chunk str;         /* used for char string type or buffers*/
 };
diff --git a/include/types/stick_table.h b/include/types/stick_table.h
index e519f49..cccf9fb 100644
--- a/include/types/stick_table.h
+++ b/include/types/stick_table.h
@@ -35,6 +35,7 @@
 /* stick table key types */
 enum {
 	STKTABLE_TYPE_IP = 0,     /* table key is ipv4 */
+	STKTABLE_TYPE_IPV6,       /* table key is ipv6 */
 	STKTABLE_TYPE_INTEGER,    /* table key is unsigned 32bit integer */
 	STKTABLE_TYPE_STRING,     /* table key is a null terminated string */
 	STKTABLE_TYPE_BINARY,     /* table key is a buffer of data  */
@@ -172,7 +173,8 @@
 
 /* stick table key data */
 union stktable_key_data {
-	struct in_addr ip;        /* used to store an ip key */
+	struct in_addr ip;        /* used to store an ipv4 key */
+	struct in6_addr ipv6;     /* used to store an ipv6 key */
 	uint32_t integer;         /* used to store an integer key */
 	char buf[BUFSIZE];        /* used to store a null terminated string key or a buffer of data */
 };
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 8b8c3fc..75baadb 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -3312,12 +3312,19 @@
 			chunk_printf(&msg, "%p:", si->applet.ctx.table.entry);
 
 			if (si->applet.ctx.table.proxy->table.type == STKTABLE_TYPE_IP) {
-				char addr[16];
+				char addr[INET_ADDRSTRLEN];
 				inet_ntop(AF_INET,
 					  (const void *)&si->applet.ctx.table.entry->key.key,
 					  addr, sizeof(addr));
 				chunk_printf(&msg, " key=%s", addr);
 			}
+			else if (si->applet.ctx.table.proxy->table.type == STKTABLE_TYPE_IPV6) {
+				char addr[INET6_ADDRSTRLEN];
+				inet_ntop(AF_INET6,
+					  (const void *)&si->applet.ctx.table.entry->key.key,
+					  addr, sizeof(addr));
+				chunk_printf(&msg, " key=%s", addr);
+			}
 			else if (si->applet.ctx.table.proxy->table.type == STKTABLE_TYPE_INTEGER) {
 				chunk_printf(&msg, " key=%u", *(unsigned int *)si->applet.ctx.table.entry->key.key);
 			}
diff --git a/src/pattern.c b/src/pattern.c
index ba8d5a0..e705f00 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -146,6 +146,32 @@
 	return 1;
 }
 
+static int c_ip2ipv6(union pattern_data *data)
+{
+	v4tov6(&data->ipv6, &data->ip);
+	return 1;
+}
+
+static int c_ipv62str(union pattern_data *data)
+{
+	struct chunk *trash = get_trash_chunk();
+
+	if (!inet_ntop(AF_INET6, (void *)&data->ipv6, trash->str, trash->size))
+		return 0;
+
+	trash->len = strlen(trash->str);
+	pattern_data_setstring(data, trash);
+
+	return 1;
+}
+
+/*
+static int c_ipv62ip(union pattern_data *data)
+{
+	return v6tov4(&data->ip, &data->ipv6);
+}
+*/
+
 static int c_int2ip(union pattern_data *data)
 {
 	data->ip.s_addr = htonl(data->integer);
@@ -159,6 +185,11 @@
 	return 1;
 }
 
+static int c_str2ipv6(union pattern_data *data)
+{
+	return inet_pton(AF_INET6, data->str.str, &data->ipv6);
+}
+
 static int c_int2str(union pattern_data *data)
 {
 	struct chunk *trash = get_trash_chunk();
@@ -222,13 +253,14 @@
 
 typedef int (*pattern_cast_fct)(union pattern_data *data);
 static pattern_cast_fct pattern_casts[PATTERN_TYPES][PATTERN_TYPES] = {
-/*            to:   IP           INTEGER      STRING       DATA         CONSTSTRING  CONSTDATA */
-/* from:    IP */ { c_donothing, c_ip2int,    c_ip2str,    NULL,        c_ip2str,    NULL        },
-/*     INTEGER */ { c_int2ip,    c_donothing, c_int2str,   NULL,        c_int2str,   NULL        },
-/*      STRING */ { c_str2ip,    c_str2int,   c_donothing, c_donothing, c_donothing, c_donothing },
-/*        DATA */ { NULL,        NULL,        NULL,        c_donothing, NULL,        c_donothing },
-/* CONSTSTRING */ { c_str2ip,    c_str2int,   c_datadup,   c_datadup,   c_donothing, c_donothing },
-/*   CONSTDATA */ { NULL,        NULL,        NULL,        c_datadup,   NULL,	     NULL        },
+/*            to:   IP           IPV6         INTEGER      STRING       DATA         CONSTSTRING  CONSTDATA */
+/* from:    IP */ { c_donothing, c_ip2ipv6,   c_ip2int,    c_ip2str,    NULL,        c_ip2str,    NULL        },
+/*        IPV6 */ { NULL,        c_donothing, NULL,        c_ipv62str,  NULL,        c_ipv62str,  NULL        },
+/*     INTEGER */ { c_int2ip,    NULL,        c_donothing, c_int2str,   NULL,        c_int2str,   NULL        },
+/*      STRING */ { c_str2ip,    c_str2ipv6,  c_str2int,   c_donothing, c_donothing, c_donothing, c_donothing },
+/*        DATA */ { NULL,        NULL,        NULL,        NULL,        c_donothing, NULL,        c_donothing },
+/* CONSTSTRING */ { c_str2ip,    c_str2ipv6,  c_str2int,   c_datadup,   c_datadup,   c_donothing, c_donothing },
+/*   CONSTDATA */ { NULL,        NULL,        NULL,        NULL,        c_datadup,   NULL,	      c_donothing },
 };
 
 
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 3c8a04f..ca993a6 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -745,7 +745,7 @@
 					 * to consider rule->act_prm->trk_ctr.type.
 					 */
 					t = rule->act_prm.trk_ctr.table.t;
-					ts = stktable_get_entry(t, tcpv4_src_to_stktable_key(s));
+					ts = stktable_get_entry(t, tcp_src_to_stktable_key(s));
 					if (ts) {
 						session_track_stkctr1(s, t, ts);
 						if (s->fe != s->be)
@@ -761,7 +761,7 @@
 					 * to consider rule->act_prm->trk_ctr.type.
 					 */
 					t = rule->act_prm.trk_ctr.table.t;
-					ts = stktable_get_entry(t, tcpv4_src_to_stktable_key(s));
+					ts = stktable_get_entry(t, tcp_src_to_stktable_key(s));
 					if (ts) {
 						session_track_stkctr2(s, t, ts);
 						if (s->fe != s->be)
@@ -915,7 +915,7 @@
 					 * to consider rule->act_prm->trk_ctr.type.
 					 */
 					t = rule->act_prm.trk_ctr.table.t;
-					ts = stktable_get_entry(t, tcpv4_src_to_stktable_key(s));
+					ts = stktable_get_entry(t, tcp_src_to_stktable_key(s));
 					if (ts)
 						session_track_stkctr1(s, t, ts);
 				}
@@ -928,7 +928,7 @@
 					 * to consider rule->act_prm->trk_ctr.type.
 					 */
 					t = rule->act_prm.trk_ctr.table.t;
-					ts = stktable_get_entry(t, tcpv4_src_to_stktable_key(s));
+					ts = stktable_get_entry(t, tcp_src_to_stktable_key(s));
 					if (ts)
 						session_track_stkctr2(s, t, ts);
 				}
@@ -1275,7 +1275,7 @@
 	return 1;
 }
 
-/* extract the connection's source address */
+/* extract the connection's source ipv4 address */
 static int
 pattern_fetch_src(struct proxy *px, struct session *l4, void *l7, int dir,
                   const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
@@ -1287,6 +1287,17 @@
 	return 1;
 }
 
+/* extract the connection's source ipv6 address */
+static int
+pattern_fetch_src6(struct proxy *px, struct session *l4, void *l7, int dir,
+                  const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
+{
+	if (l4->si[0].addr.c.from.ss_family != AF_INET6)
+		return 0;
+
+	memcpy(data->ipv6.s6_addr, ((struct sockaddr_in6 *)&l4->si[0].addr.c.from)->sin6_addr.s6_addr, sizeof(data->ipv6.s6_addr));
+	return 1;
+}
 
 /* set test->i to the connection's source port */
 static int
@@ -1326,7 +1337,7 @@
 }
 
 
-/* extract the connection's destination address */
+/* extract the connection's destination ipv4 address */
 static int
 pattern_fetch_dst(struct proxy *px, struct session *l4, void *l7, int dir,
                   const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
@@ -1341,6 +1352,21 @@
 	return 1;
 }
 
+/* extract the connection's destination ipv6 address */
+static int
+pattern_fetch_dst6(struct proxy *px, struct session *l4, void *l7, int dir,
+                  const struct pattern_arg *arg_p, int arg_i, union pattern_data *data)
+{
+	if (!(l4->flags & SN_FRT_ADDR_SET))
+		get_frt_addr(l4);
+
+	if (l4->si[0].addr.c.to.ss_family != AF_INET6)
+		return 0;
+
+	memcpy(data->ipv6.s6_addr, ((struct sockaddr_in6 *)&l4->si[0].addr.c.to)->sin6_addr.s6_addr, sizeof(data->ipv6.s6_addr));
+	return 1;
+}
+
 /* set test->i to the frontend connexion's destination port */
 static int
 acl_fetch_dport(struct proxy *px, struct session *l4, void *l7, int dir,
@@ -1367,10 +1393,13 @@
 	if (!(l4->flags & SN_FRT_ADDR_SET))
 		get_frt_addr(l4);
 
-	if (l4->si[0].addr.c.to.ss_family != AF_INET)
+	if (l4->si[0].addr.c.to.ss_family == AF_INET)
+		data->integer = ntohs(((struct sockaddr_in *)&l4->si[0].addr.c.to)->sin_port);
+	else if (l4->si[0].addr.c.to.ss_family == AF_INET6)
+		data->integer = ntohs(((struct sockaddr_in6 *)&l4->si[0].addr.c.to)->sin6_port);
+	else
 		return 0;
 
-	data->integer = ntohs(((struct sockaddr_in *)&l4->si[0].addr.c.to)->sin_port);
 	return 1;
 }
 
@@ -1566,7 +1595,9 @@
 /* Note: must not be declared <const> as its list will be overwritten */
 static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{
 	{ "src",         pattern_fetch_src,       NULL,                         PATTERN_TYPE_IP,        PATTERN_FETCH_REQ },
+	{ "src6",        pattern_fetch_src6,      NULL,                         PATTERN_TYPE_IPV6,      PATTERN_FETCH_REQ },
 	{ "dst",         pattern_fetch_dst,       NULL,                         PATTERN_TYPE_IP,        PATTERN_FETCH_REQ },
+	{ "dst6",        pattern_fetch_dst6,      NULL,                         PATTERN_TYPE_IPV6,      PATTERN_FETCH_REQ },
 	{ "dst_port",    pattern_fetch_dport,     NULL,                         PATTERN_TYPE_INTEGER,   PATTERN_FETCH_REQ },
 	{ "payload",     pattern_fetch_payload,   pattern_arg_fetch_payload,    PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR },
 	{ "payload_lv",  pattern_fetch_payloadlv, pattern_arg_fetch_payloadlv,  PATTERN_TYPE_CONSTDATA, PATTERN_FETCH_REQ|PATTERN_FETCH_RTR },
diff --git a/src/session.c b/src/session.c
index b7914d0..27f4fbd 100644
--- a/src/session.c
+++ b/src/session.c
@@ -2244,9 +2244,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2307,9 +2307,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2366,9 +2366,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2430,9 +2430,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2454,9 +2454,9 @@
 	struct stktable_key *key;
 	void *ptr;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2524,9 +2524,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2583,9 +2583,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2647,9 +2647,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2706,9 +2706,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2770,9 +2770,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2829,9 +2829,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2893,9 +2893,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -2957,9 +2957,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -3023,9 +3023,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -3087,9 +3087,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
@@ -3153,9 +3153,9 @@
 {
 	struct stktable_key *key;
 
-	key = tcpv4_src_to_stktable_key(l4);
+	key = tcp_src_to_stktable_key(l4);
 	if (!key)
-		return 0; /* only TCPv4 is supported right now */
+		return 0;
 
 	if (expr->arg_len)
 		px = find_stktable(expr->arg.str);
diff --git a/src/standard.c b/src/standard.c
index 0e5c71e..d3b9ef7 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -1199,6 +1199,54 @@
 	return __full_hash(a);
 }
 
+/* Return non-zero if IPv4 address is part of the network,
+ * otherwise zero.
+ */
+int in_net_ipv4(struct in_addr *addr, struct in_addr *mask, struct in_addr *net)
+{
+	return((addr->s_addr & mask->s_addr) == (net->s_addr & mask->s_addr));
+}
+
+/* Return non-zero if IPv6 address is part of the network,
+ * otherwise zero.
+ */
+int in_net_ipv6(struct in6_addr *addr, struct in6_addr *mask, struct in6_addr *net)
+{
+	int i;
+
+	for (i = 0; i < sizeof(struct in6_addr) / sizeof(int); i++)
+		if (((((int *)addr)[i] & ((int *)mask)[i])) !=
+		    (((int *)net)[i] & ((int *)mask)[i]))
+			return 0;
+	return 1;
+}
+
+/* RFC 4291 prefix */
+const char rfc4291_pfx[] = { 0x00, 0x00, 0x00, 0x00,
+			     0x00, 0x00, 0x00, 0x00,
+			     0x00, 0x00, 0xFF, 0xFF };
+
+/* Map IPv4 adress on IPv6 address, as specified in RFC 3513. */
+void v4tov6(struct in6_addr *sin6_addr, struct in_addr *sin_addr)
+{
+	memcpy(sin6_addr->s6_addr, rfc4291_pfx, sizeof(rfc4291_pfx));
+	memcpy(sin6_addr->s6_addr+12, &sin_addr->s_addr, 4);
+}
+
+/* Map IPv6 adress on IPv4 address, as specified in RFC 3513.
+ * Return true if conversion is possible and false otherwise.
+ */
+int v6tov4(struct in_addr *sin_addr, struct in6_addr *sin6_addr)
+{
+	if (memcmp(sin6_addr->s6_addr, rfc4291_pfx, sizeof(rfc4291_pfx)) == 0) {
+		memcpy(&(sin_addr->s_addr), &(sin6_addr->s6_addr[12]),
+			sizeof(struct in_addr));
+		return 1;
+	}
+
+	return 0;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
diff --git a/src/stick_table.c b/src/stick_table.c
index 7c9ad8d..5e9aa35 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -406,9 +406,10 @@
  * Configuration keywords of known table types
  */
 struct stktable_type stktable_types[STKTABLE_TYPES] =  {{ "ip", 0, 4 },
+						        { "ipv6", 0, 16 },
 						        { "integer", 0, 4 },
 						        { "string", STK_F_CUSTOM_KEYSIZE, 32 },
-							{ "binary", STK_F_CUSTOM_KEYSIZE, 32 } };
+						        { "binary", STK_F_CUSTOM_KEYSIZE, 32 } };
 
 
 /*
@@ -457,6 +458,25 @@
 	return (void *)&pdata->ip.s_addr;
 }
 
+static void *k_ip2ipv6(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
+{
+	v4tov6(&pdata->ipv6, &pdata->ip);
+	return (void *)&pdata->ipv6.s6_addr;
+}
+
+static void *k_ipv62ipv6(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
+{
+	return (void *)&pdata->ipv6.s6_addr;
+}
+
+/*
+static void *k_ipv62ip(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
+{
+	v6tov4(&pdata->ip, &pdata->ipv6);
+	return (void *)&pdata->ip.s_addr;
+}
+*/
+
 static void *k_ip2int(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
 {
 	kdata->integer = ntohl(pdata->ip.s_addr);
@@ -484,6 +504,15 @@
 	return (void *)kdata->buf;
 }
 
+static void *k_ipv62str(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
+{
+	if (!inet_ntop(AF_INET6, &pdata->ipv6, kdata->buf, sizeof(kdata->buf)))
+		return NULL;
+
+	*len = strlen((const char *)kdata->buf);
+	return (void *)kdata->buf;
+}
+
 static void *k_int2str(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
 {
 	void *key;
@@ -504,6 +533,13 @@
 	return (void *)&kdata->ip.s_addr;
 }
 
+static void *k_str2ipv6(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
+{
+	if (!inet_pton(AF_INET6, pdata->str.str, &kdata->ipv6))
+		return NULL;
+
+	return (void *)&kdata->ipv6.s6_addr;
+}
 
 static void *k_str2int(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len)
 {
@@ -527,15 +563,22 @@
 /*         NULL pointer used for impossible pattern casts        */
 /*****************************************************************/
 
+/*
+ * Conversions from IPv6 to IPv4 are available, but we haven't
+ * added them to the table since they doesn't seem sufficely
+ * relevant and could cause confusion in configuration.
+ */
+
 typedef void *(*pattern_to_key_fct)(union pattern_data *pdata, union stktable_key_data *kdata, size_t *len);
 static pattern_to_key_fct pattern_to_key[PATTERN_TYPES][STKTABLE_TYPES] = {
-/*         table type:   IP        INTEGER    STRING     BINARY    */
-/* pattern type: IP */ { k_ip2ip,  k_ip2int,  k_ip2str,  NULL      },
-/*          INTEGER */ { k_int2ip, k_int2int, k_int2str, NULL      },
-/*           STRING */ { k_str2ip, k_str2int, k_str2str, k_str2str },
-/*             DATA */ { NULL,     NULL,      NULL,      k_str2str },
-/*      CONSTSTRING */ { k_str2ip, k_str2int, k_str2str, k_str2str },
-/*        CONSTDATA */ { NULL,     NULL,      NULL,      k_str2str },
+/*       table type:   IP          IPV6         INTEGER    STRING      BINARY    */
+/* pattern type: IP */ { k_ip2ip,  k_ip2ipv6,   k_ip2int,  k_ip2str,   NULL      },
+/*             IPV6 */ { NULL,     k_ipv62ipv6, NULL,      k_ipv62str, NULL      },
+/*          INTEGER */ { k_int2ip, NULL,        k_int2int, k_int2str,  NULL      },
+/*           STRING */ { k_str2ip, k_str2ipv6,  k_str2int, k_str2str,  k_str2str },
+/*             DATA */ { NULL,     NULL,        NULL,      NULL,       k_str2str },
+/*      CONSTSTRING */ { k_str2ip, k_str2ipv6,  k_str2int, k_str2str,  k_str2str },
+
 };