aboutsummaryrefslogtreecommitdiffstats
path: root/test/packetdrill/ip_prefix.c
blob: 044b94d6285051b8f775e3f667b8861f0fb613e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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;
}