From e6223b9cfd62091f227ea46978021a292ab2140d Mon Sep 17 00:00:00 2001
From: Neal Cardwell <ncardwell@google.com>
Date: Thu, 28 Nov 2013 13:12:45 -0500
Subject: [PATCH] net-test: packetdrill: support IP prefixes

The goal here is to be able to route a prefix of user-configurable
size over the tun device or to the remote packetdrill server, so that
tests can use more than one remote IP. The immediate motivation is
testing GRE, where there is a remote IP for the TCP endpoint and a
separate remote IP for the GRE decapsulator.

Change-Id: I36b7025a242c3803802087c91d284a41c62ca1bd
---
 gtests/net/packetdrill/Makefile.common |   3 +-
 gtests/net/packetdrill/ip_address.h    |   1 +
 gtests/net/packetdrill/ip_prefix.c     | 148 +++++++++++++++++++++++++
 gtests/net/packetdrill/ip_prefix.h     |  69 ++++++++++++
 gtests/net/packetdrill/types.h         |   4 +-
 5 files changed, 222 insertions(+), 3 deletions(-)
 create mode 100644 gtests/net/packetdrill/ip_prefix.c
 create mode 100644 gtests/net/packetdrill/ip_prefix.h

diff --git a/gtests/net/packetdrill/Makefile.common b/gtests/net/packetdrill/Makefile.common
index 1f2d45ab..89e3e19a 100644
--- a/gtests/net/packetdrill/Makefile.common
+++ b/gtests/net/packetdrill/Makefile.common
@@ -10,7 +10,8 @@ lexer.o: lexer.l parser.o
 	flex -olexer.c lexer.l
 	$(CC) -O2 -g -Wall -c lexer.c
 
-packetdrill-lib := checksum.o code.o config.o hash.o hash_map.o ip_address.o \
+packetdrill-lib := \
+         checksum.o code.o config.o hash.o hash_map.o ip_address.o ip_prefix.o \
          netdev.o net_utils.o \
          packet.o packet_socket_linux.o packet_socket_pcap.o \
          packet_checksum.o packet_parser.o packet_to_string.o \
diff --git a/gtests/net/packetdrill/ip_address.h b/gtests/net/packetdrill/ip_address.h
index 596f5f93..1c5b8a41 100644
--- a/gtests/net/packetdrill/ip_address.h
+++ b/gtests/net/packetdrill/ip_address.h
@@ -35,6 +35,7 @@ struct ip_address {
 	union {
 		struct in_addr v4;
 		struct in6_addr v6;
+		u8 bytes[16];
 	} ip;				/* IP address (network order) */
 };
 
diff --git a/gtests/net/packetdrill/ip_prefix.c b/gtests/net/packetdrill/ip_prefix.c
new file mode 100644
index 00000000..044b94d6
--- /dev/null
+++ b/gtests/net/packetdrill/ip_prefix.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+/*
+ * Author: ncardwell@google.com (Neal Cardwell)
+ *
+ * Implementation for operations for IPv4 and IPv6 prefixes.
+ */
+
+#include "ip_prefix.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "logging.h"
+
+struct ip_prefix ip_to_prefix(const struct ip_address *ip, int prefix_len)
+{
+	int max_prefix_bits = 8 * ip_address_length(ip->address_family);
+	struct ip_prefix prefix;
+
+	if (prefix_len < 0 || prefix_len > max_prefix_bits)
+		die("invalid prefix_len: %d bits", prefix_len);
+
+	prefix.ip = *ip;
+	prefix.prefix_len = prefix_len;
+
+	return prefix;
+}
+
+void ip_prefix_normalize(struct ip_prefix *prefix)
+{
+	/* Find the byte and bit offset where the prefix ends. */
+	int bytes = prefix->prefix_len / 8;
+	int bits = prefix->prefix_len % 8;
+	int max_prefix_bytes = ip_address_length(prefix->ip.address_family);
+
+	/* Zero the bits beyond the prefix in the byte where it ends. */
+	if (bits != 0) {
+		int pos = 8 - bits;
+		prefix->ip.ip.bytes[bytes] &= ~((1U << pos) - 1);
+		++bytes;
+
+	}
+	/* Zero out the rest of the bytes in the address. */
+	memset(prefix->ip.ip.bytes + bytes, 0, max_prefix_bytes - bytes);
+}
+
+/* Parse and return a prefix length (in bits) like /16 or /64 from the
+ * end of a string, and die if the prefix is bigger than the given max
+ * length. Use the maximum length if there is no prefix in the string.
+ */
+static int prefix_len_parse(const char *prefix_string, int max_len)
+{
+	int prefix_len = 0;
+	const char *len_str = NULL;
+
+	len_str = strstr(prefix_string, "/");
+	if (len_str != NULL) {
+		/* Parse prefix len in string */
+		char *end = NULL;
+
+		++len_str;		/* advance beyond '/' */
+		errno = 0;
+		prefix_len = strtol(len_str, &end, 10);
+
+		if (errno != 0 || *end != '\0' ||
+		    (prefix_len < 0) || (prefix_len > max_len))
+			die("bad prefix length in prefix '%s'\n",
+			    prefix_string);
+	} else {
+		/* Default prefix length is all address bits */
+		prefix_len = max_len;
+	}
+
+	return prefix_len;
+}
+
+/* Copy the address part of a "<address>/<prefix>" string. */
+static char *copy_prefix_address(const char *prefix_string)
+{
+	const char *slash = strstr(prefix_string, "/");
+	int len = 0;
+	if (slash != NULL)
+		len = slash - prefix_string;
+	else
+		len = strlen(prefix_string);
+	return strndup(prefix_string, len);
+}
+
+struct ip_prefix ipv4_prefix_parse(const char *prefix_string)
+{
+	char *ip_str = copy_prefix_address(prefix_string);
+	struct ip_address ip = ipv4_parse(ip_str);
+	int prefix_len = prefix_len_parse(prefix_string,
+					  8 * ip_address_length(AF_INET));
+
+	free(ip_str);
+
+	return ip_to_prefix(&ip, prefix_len);
+}
+
+struct ip_prefix ipv6_prefix_parse(const char *prefix_string)
+{
+	char *ip_str = copy_prefix_address(prefix_string);
+	struct ip_address ip = ipv6_parse(ip_str);
+	int prefix_len = prefix_len_parse(prefix_string,
+					  8 * ip_address_length(AF_INET6));
+
+	free(ip_str);
+
+	return ip_to_prefix(&ip, prefix_len);
+}
+
+const char *ip_prefix_to_string(struct ip_prefix *prefix, char *buffer)
+{
+	char ip_str[ADDR_STR_LEN];
+	int bytes = 0;
+
+	memset(ip_str, 0, sizeof(ip_str));
+	ip_to_string(&prefix->ip, ip_str);
+
+	if (strlen(ip_str) + strlen("/128") + 1 > ADDR_STR_LEN)
+		die("address prefix would overflow buffer!");
+
+	bytes = snprintf(buffer, ADDR_STR_LEN, "%s/%d",
+			 ip_str, prefix->prefix_len);
+	if (bytes >= ADDR_STR_LEN)
+		die("address prefix overflowed buffer!");
+
+	return buffer;
+}
diff --git a/gtests/net/packetdrill/ip_prefix.h b/gtests/net/packetdrill/ip_prefix.h
new file mode 100644
index 00000000..0b82260d
--- /dev/null
+++ b/gtests/net/packetdrill/ip_prefix.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+/*
+ * Author: ncardwell@google.com (Neal Cardwell)
+ *
+ * Types and operations for IPv4 and IPv6 address prefixes.
+ */
+
+#ifndef __IP_PREFIX_H__
+#define __IP_PREFIX_H__
+
+#include "types.h"
+
+#include "ip_address.h"
+
+/* IPv4 or IPv6 address prefix. */
+struct ip_prefix {
+	struct ip_address ip;
+	int prefix_len;			/* prefix length in bits */
+};
+
+static inline void ip_prefix_reset(struct ip_prefix *prefix)
+{
+	memset(prefix, 0, sizeof(*prefix));
+}
+
+/* Parse a human-readable IPv4 prefix and return it. Print an error
+ * to stderr and exit if there is an error parsing the prefix.
+ */
+extern struct ip_prefix ipv4_prefix_parse(const char *prefix_string);
+
+/* Parse a human-readable IPv6 prefix and return it. Print an error
+ * to stderr and exit if there is an error parsing the prefix.
+ */
+extern struct ip_prefix ipv6_prefix_parse(const char *prefix_string);
+
+/* Fill in the given prefix using the first 'prefix_len' bits of the
+ * given IP address, zeroing out bits beyond the prefix length.
+ */
+extern struct ip_prefix ip_to_prefix(const struct ip_address *ip,
+				     int prefix_len);
+
+/* Zero the bits beyond the prefix length. */
+void ip_prefix_normalize(struct ip_prefix *prefix);
+
+/* Print a human-readable representation of the given IP prefix in the
+ * given buffer, which must be at least ADDR_STR_LEN bytes long.
+ * Returns a pointer to the given buffer.
+ */
+extern const char *ip_prefix_to_string(struct ip_prefix *prefix,
+				       char *buffer);
+
+#endif /* __IP_PREFIX_H__ */
diff --git a/gtests/net/packetdrill/types.h b/gtests/net/packetdrill/types.h
index 289f6af0..50d7edf2 100644
--- a/gtests/net/packetdrill/types.h
+++ b/gtests/net/packetdrill/types.h
@@ -122,8 +122,8 @@ enum ip_ecn_t {
 	ECN_NOCHECK,
 };
 
-/* Length of output buffer for inet_ntop. */
-#define ADDR_STR_LEN ((INET_ADDRSTRLEN + INET6_ADDRSTRLEN)+1)
+/* Length of output buffer for inet_ntop, plus prefix length (e.g. "/128"). */
+#define ADDR_STR_LEN ((INET_ADDRSTRLEN + INET6_ADDRSTRLEN)+5)
 
 /* Flavors of IP versions we support. */
 enum ip_version_t {
-- 
GitLab