Index: bgpd/util.c
===================================================================
RCS file: /home/cvs/private/hrs/openbgpd/bgpd/util.c,v
retrieving revision 1.1.1.6
retrieving revision 1.7
diff -u -p -r1.1.1.6 -r1.7
--- bgpd/util.c	14 Feb 2010 20:19:57 -0000	1.1.1.6
+++ bgpd/util.c	13 Oct 2012 18:36:00 -0000	1.7
@@ -1,4 +1,4 @@
-/*	$OpenBSD: util.c,v 1.6 2009/06/12 16:42:53 claudio Exp $ */
+/*	$OpenBSD: util.c,v 1.11 2010/03/29 09:04:43 claudio Exp $ */
 
 /*
  * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
@@ -18,6 +18,9 @@
  */
 #include <sys/types.h>
 #include <sys/socket.h>
+#if defined(__FreeBSD__)	/* sys/limits.h */
+#include <sys/limits.h>
+#endif /* defined(__FreeBSD__) */
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -28,15 +31,30 @@
 #include "bgpd.h"
 #include "rde.h"
 
+const char	*aspath_delim(u_int8_t, int);
+
 const char *
 log_addr(const struct bgpd_addr *addr)
 {
 	static char	buf[48];
+	char		tbuf[16];
 
-	if (inet_ntop(addr->af, &addr->ba, buf, sizeof(buf)) == NULL)
-		return ("?");
-	else
+	switch (addr->aid) {
+	case AID_INET:
+	case AID_INET6:
+		if (inet_ntop(aid2af(addr->aid), &addr->ba, buf,
+		    sizeof(buf)) == NULL)
+			return ("?");
 		return (buf);
+	case AID_VPN_IPv4:
+		if (inet_ntop(AF_INET, &addr->vpn4.addr, tbuf,
+		    sizeof(tbuf)) == NULL)
+			return ("?");
+		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn4.rd),
+		   tbuf);
+		return (buf);
+	}
+	return ("???");
 }
 
 const char *
@@ -90,6 +108,96 @@ log_as(u_int32_t as)
 	return (buf);
 }
 
+const char *
+log_rd(u_int64_t rd)
+{
+	static char	buf[32];
+	struct in_addr	addr;
+	u_int32_t	u32;
+	u_int16_t	u16;
+
+	rd = betoh64(rd);
+	switch (rd >> 48) {
+	case EXT_COMMUNITY_TWO_AS:
+		u32 = rd & 0xffffffff;
+		u16 = (rd >> 32) & 0xffff;
+		snprintf(buf, sizeof(buf), "rd %i:%i", u16, u32);
+		break;
+	case EXT_COMMUNITY_FOUR_AS:
+		u32 = (rd >> 16) & 0xffffffff;
+		u16 = rd & 0xffff;
+		snprintf(buf, sizeof(buf), "rd %s:%i", log_as(u32), u16);
+		break;
+	case EXT_COMMUNITY_IPV4:
+		u32 = (rd >> 16) & 0xffffffff;
+		u16 = rd & 0xffff;
+		addr.s_addr = htonl(u32);
+		snprintf(buf, sizeof(buf), "rd %s:%i", inet_ntoa(addr), u16);
+		break;
+	default:
+		return ("rd ?");
+	}
+	return (buf);
+}
+
+/* NOTE: this function does not check if the type/subtype combo is
+ * actually valid. */
+const char *
+log_ext_subtype(u_int8_t subtype)
+{
+	static char etype[6];
+
+	switch (subtype) {
+	case EXT_COMMUNITY_ROUTE_TGT:
+		return ("rt");	/* route target */
+	case EXT_CUMMUNITY_ROUTE_ORIG:
+		return ("soo");	/* source of origin */
+	case EXT_COMMUNITY_OSPF_DOM_ID:
+		return ("odi");	/* ospf domain id */
+	case EXT_COMMUNITY_OSPF_RTR_TYPE:
+		return ("ort");	/* ospf route type */
+	case EXT_COMMUNITY_OSPF_RTR_ID:
+		return ("ori");	/* ospf router id */
+	case EXT_COMMUNITY_BGP_COLLECT:
+		return ("bdc");	/* bgp data collection */
+	default:
+		snprintf(etype, sizeof(etype), "[%u]", subtype);
+		return (etype);
+	}
+}
+
+const char *
+aspath_delim(u_int8_t seg_type, int closing)
+{
+	static char db[8];
+
+	switch (seg_type) {
+	case AS_SET:
+		if (!closing)
+			return ("{ ");
+		else
+			return (" }");
+	case AS_SEQUENCE:
+		return ("");
+	case AS_CONFED_SEQUENCE:
+		if (!closing)
+			return ("( ");
+		else
+			return (" )");
+	case AS_CONFED_SET:
+		if (!closing)
+			return ("[ ");
+		else
+			return (" ]");
+	default:
+		if (!closing)
+			snprintf(db, sizeof(db), "!%u ", seg_type);
+		else
+			snprintf(db, sizeof(db), " !%u", seg_type);
+		return (db);
+	}
+}
+
 int
 aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
 {
@@ -118,16 +226,10 @@ aspath_snprint(char *buf, size_t size, v
 		seg_len = seg[1];
 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
 
-		if (seg_type == AS_SET) {
-			if (total_size != 0)
-				r = snprintf(buf, size, " { ");
-			else
-				r = snprintf(buf, size, "{ ");
-			UPDATE();
-		} else if (total_size != 0) {
-			r = snprintf(buf, size, " ");
-			UPDATE();
-		}
+		r = snprintf(buf, size, "%s%s",
+		    total_size != 0 ? " " : "",
+		    aspath_delim(seg_type, 0));
+		UPDATE();
 
 		for (i = 0; i < seg_len; i++) {
 			r = snprintf(buf, size, "%s",
@@ -138,10 +240,8 @@ aspath_snprint(char *buf, size_t size, v
 				UPDATE();
 			}
 		}
-		if (seg_type == AS_SET) {
-			r = snprintf(buf, size, " }");
-			UPDATE();
-		}
+		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
+		UPDATE();
 	}
 	/* ensure that we have a valid C-string especially for empty as path */
 	if (size > 0)
@@ -235,6 +335,67 @@ aspath_strlen(void *data, u_int16_t len)
 	return (total_size);
 }
 
+/* we need to be able to search more than one as */
+int
+aspath_match(void *data, u_int16_t len, enum as_spec type, u_int32_t as)
+{
+	u_int8_t	*seg;
+	int		 final;
+	u_int16_t	 seg_size;
+	u_int8_t	 i, seg_type, seg_len;
+
+	if (type == AS_EMPTY) {
+		if (len == 0)
+			return (1);
+		else
+			return (0);
+	}
+
+	final = 0;
+	seg = data;
+	for (; len > 0; len -= seg_size, seg += seg_size) {
+		seg_type = seg[0];
+		seg_len = seg[1];
+		seg_size = 2 + sizeof(u_int32_t) * seg_len;
+
+		final = (len == seg_size);
+
+		/* just check the first (leftmost) AS */
+		if (type == AS_PEER) {
+			if (as == aspath_extract(seg, 0))
+				return (1);
+			else
+				return (0);
+		}
+		/* just check the final (rightmost) AS */
+		if (type == AS_SOURCE) {
+			/* not yet in the final segment */
+			if (!final)
+				continue;
+
+			if (as == aspath_extract(seg, seg_len - 1))
+				return (1);
+			else
+				return (0);
+		}
+
+		/* AS_TRANSIT or AS_ALL */
+		for (i = 0; i < seg_len; i++) {
+			if (as == aspath_extract(seg, i)) {
+				/*
+				 * the source (rightmost) AS is excluded from
+				 * AS_TRANSIT matches.
+				 */
+				if (final && i == seg_len - 1 &&
+				    type == AS_TRANSIT)
+					return (0);
+				return (1);
+			}
+		}
+	}
+	return (0);
+}
+
 /*
  * Extract the asnum out of the as segment at the specified position.
  * Direct access is not possible because of non-aligned reads.
@@ -251,6 +412,66 @@ aspath_extract(const void *seg, int pos)
 	return (ntohl(as));
 }
 
+int
+prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
+    int prefixlen)
+{
+	in_addr_t	mask, aa, ba;
+	int		i;
+	u_int8_t	m;
+
+	if (a->aid != b->aid)
+		return (a->aid - b->aid);
+
+	switch (a->aid) {
+	case AID_INET:
+		if (prefixlen > 32)
+			fatalx("prefix_cmp: bad IPv4 prefixlen");
+		mask = htonl(prefixlen2mask(prefixlen));
+		aa = ntohl(a->v4.s_addr & mask);
+		ba = ntohl(b->v4.s_addr & mask);
+		if (aa != ba)
+			return (aa - ba);
+		return (0);
+	case AID_INET6:
+		if (prefixlen > 128)
+			fatalx("prefix_cmp: bad IPv6 prefixlen");
+		for (i = 0; i < prefixlen / 8; i++)
+			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
+				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
+		i = prefixlen % 8;
+		if (i) {
+			m = 0xff00 >> i;
+			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
+			    (b->v6.s6_addr[prefixlen / 8] & m))
+				return ((a->v6.s6_addr[prefixlen / 8] & m) -
+				    (b->v6.s6_addr[prefixlen / 8] & m));
+		}
+		return (0);
+	case AID_VPN_IPv4:
+		if (prefixlen > 32)
+			fatalx("prefix_cmp: bad IPv4 VPN prefixlen");
+		if (betoh64(a->vpn4.rd) > betoh64(b->vpn4.rd))
+			return (1);
+		if (betoh64(a->vpn4.rd) < betoh64(b->vpn4.rd))
+			return (-1);
+		mask = htonl(prefixlen2mask(prefixlen));
+		aa = ntohl(a->vpn4.addr.s_addr & mask);
+		ba = ntohl(b->vpn4.addr.s_addr & mask);
+		if (aa != ba)
+			return (aa - ba);
+		if (a->vpn4.labellen > b->vpn4.labellen)
+			return (1);
+		if (a->vpn4.labellen < b->vpn4.labellen)
+			return (-1);
+		return (memcmp(a->vpn4.labelstack, b->vpn4.labelstack,
+		    a->vpn4.labellen));
+	default:
+		fatalx("prefix_cmp: unknown af");
+	}
+	return (-1);
+}
+
 in_addr_t
 prefixlen2mask(u_int8_t prefixlen)
 {
@@ -276,3 +497,115 @@ inet6applymask(struct in6_addr *dest, co
 	for (i = 0; i < 16; i++)
 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
 }
+
+/* address family translation functions */
+const struct aid aid_vals[AID_MAX] = AID_VALS;
+
+const char *
+aid2str(u_int8_t aid)
+{
+	if (aid < AID_MAX)
+		return (aid_vals[aid].name);
+	return ("unknown AID");
+}
+
+int
+aid2afi(u_int8_t aid, u_int16_t *afi, u_int8_t *safi)
+{
+	if (aid < AID_MAX) {
+		*afi = aid_vals[aid].afi;
+		*safi = aid_vals[aid].safi;
+		return (0);
+	}
+	return (-1);
+}
+
+int
+afi2aid(u_int16_t afi, u_int8_t safi, u_int8_t *aid)
+{
+	u_int8_t i;
+
+	for (i = 0; i < AID_MAX; i++)
+		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
+			*aid = i;
+			return (0);
+		}
+
+	return (-1);
+}
+
+sa_family_t
+aid2af(u_int8_t aid)
+{
+	if (aid < AID_MAX)
+		return (aid_vals[aid].af);
+	return (AF_UNSPEC);
+}
+
+int
+af2aid(sa_family_t af, u_int8_t safi, u_int8_t *aid)
+{
+	u_int8_t i;
+
+	if (safi == 0) /* default to unicast subclass */
+		safi = SAFI_UNICAST;
+
+	for (i = 0; i < AID_MAX; i++)
+		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
+			*aid = i;
+			return (0);
+		}
+
+	return (-1);
+}
+
+struct sockaddr *
+addr2sa(struct bgpd_addr *addr, u_int16_t port)
+{
+	static struct sockaddr_storage	 ss;
+	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
+	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
+
+	if (addr->aid == AID_UNSPEC)
+		return (NULL);
+
+	bzero(&ss, sizeof(ss));
+	switch (addr->aid) {
+	case AID_INET:
+		sa_in->sin_family = AF_INET;
+		sa_in->sin_len = sizeof(struct sockaddr_in);
+		sa_in->sin_addr.s_addr = addr->v4.s_addr;
+		sa_in->sin_port = htons(port);
+		break;
+	case AID_INET6:
+		sa_in6->sin6_family = AF_INET6;
+		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+		memcpy(&sa_in6->sin6_addr, &addr->v6,
+		    sizeof(sa_in6->sin6_addr));
+		sa_in6->sin6_port = htons(port);
+		sa_in6->sin6_scope_id = addr->scope_id;
+		break;
+	}
+
+	return ((struct sockaddr *)&ss);
+}
+
+void
+sa2addr(struct sockaddr *sa, struct bgpd_addr *addr)
+{
+	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
+	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
+
+	bzero(addr, sizeof(*addr));
+	switch (sa->sa_family) {
+	case AF_INET:
+		addr->aid = AID_INET;
+		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
+		break;
+	case AF_INET6:
+		addr->aid = AID_INET6;
+		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
+		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
+		break;
+	}
+}