summaryrefslogtreecommitdiff
path: root/usr/src/lib/libinetutil/common/inetutil.c
blob: f336f95720dc726b106da4baee189fa4c00388de (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <unistd.h>
#include <netinet/in.h>
#include <libinetutil.h>
#include <inet/ip.h>
#include <strings.h>
#include <stddef.h>
#include <errno.h>
#include <libsocket_priv.h>

/*
 * Internet utility functions.
 */

/*
 * Given a host-order address, calculate client's default net mask.
 * Consult netmasks database to see if net is further subnetted.
 * We'll only snag the first netmask that matches our criteria.
 * We return the resultant netmask in host order.
 */
void
get_netmask4(const struct in_addr *n_addrp, struct in_addr *s_addrp)
{
	struct in_addr	hp, tp;

	/*
	 * First check if VLSM is in use.
	 */
	hp.s_addr = htonl(n_addrp->s_addr);
	if (getnetmaskbyaddr(hp, &tp) == 0) {
		s_addrp->s_addr = ntohl(tp.s_addr);
		return;
	}

	/*
	 * Fall back on standard classed networks.
	 */
	if (IN_CLASSA(n_addrp->s_addr))
		s_addrp->s_addr = IN_CLASSA_NET;
	else if (IN_CLASSB(n_addrp->s_addr))
		s_addrp->s_addr = IN_CLASSB_NET;
	else if (IN_CLASSC(n_addrp->s_addr))
		s_addrp->s_addr = IN_CLASSC_NET;
	else
		s_addrp->s_addr = IN_CLASSE_NET;
}

/*
 * Checks if the IP addresses `ssp1' and `ssp2' are equal.
 */
boolean_t
sockaddrcmp(const struct sockaddr_storage *ssp1,
    const struct sockaddr_storage *ssp2)
{
	struct in_addr addr1, addr2;
	const struct in6_addr *addr6p1, *addr6p2;

	if (ssp1->ss_family != ssp2->ss_family)
		return (B_FALSE);

	if (ssp1 == ssp2)
		return (B_TRUE);

	switch (ssp1->ss_family) {
	case AF_INET:
		addr1 = ((const struct sockaddr_in *)ssp1)->sin_addr;
		addr2 = ((const struct sockaddr_in *)ssp2)->sin_addr;
		return (addr1.s_addr == addr2.s_addr);
	case AF_INET6:
		addr6p1 = &((const struct sockaddr_in6 *)ssp1)->sin6_addr;
		addr6p2 = &((const struct sockaddr_in6 *)ssp2)->sin6_addr;
		return (IN6_ARE_ADDR_EQUAL(addr6p1, addr6p2));
	}
	return (B_FALSE);
}

/*
 * Stores the netmask in `mask' for the given prefixlen `plen' and also sets
 * `sa_family' in `mask'. Because this function does not require aligned
 * access to the data inside of the sockaddr_in/6 structures, the code can
 * use offsetof() to find the right place in the incoming structure. Why is
 * using that beneficial? Less issues with lint. When using a direct cast
 * of the struct sockaddr_storage structure to sockaddr_in6, a lint warning
 * is generated because the former is composed of 16bit & 8bit elements whilst
 * sockaddr_in6 has a 32bit alignment requirement.
 */
int
plen2mask(uint_t prefixlen, sa_family_t af, struct sockaddr *mask)
{
	uint8_t	*addr;

	if (af == AF_INET) {
		if (prefixlen > IP_ABITS)
			return (EINVAL);
		bzero(mask, sizeof (struct sockaddr_in));
		addr = (uint8_t *)mask;
		addr += offsetof(struct sockaddr_in, sin_addr);
	} else {
		if (prefixlen > IPV6_ABITS)
			return (EINVAL);
		bzero(mask, sizeof (struct sockaddr_in6));
		addr = (uint8_t *)mask;
		addr += offsetof(struct sockaddr_in6, sin6_addr);
	}
	mask->sa_family = af;

	while (prefixlen > 0) {
		if (prefixlen >= 8) {
			*addr++ = 0xFF;
			prefixlen -= 8;
			continue;
		}
		*addr |= 1 << (8 - prefixlen);
		prefixlen--;
	}
	return (0);
}

/*
 * Convert a mask to a prefix length.
 * Returns prefix length on success, -1 otherwise.
 * The comments (above) for plen2mask about the use of `mask' also apply
 * to this function and the choice to use offsetof here too.
 */
int
mask2plen(const struct sockaddr *mask)
{
	int rc = 0;
	uint8_t last;
	uint8_t *addr;
	int limit;

	if (mask->sa_family == AF_INET) {
		limit = IP_ABITS;
		addr = (uint8_t *)mask;
		addr += offsetof(struct sockaddr_in, sin_addr);
	} else {
		limit = IPV6_ABITS;
		addr = (uint8_t *)mask;
		addr += offsetof(struct sockaddr_in6, sin6_addr);
	}

	while (*addr == 0xff) {
		rc += 8;
		if (rc == limit)
			return (limit);
		addr++;
	}

	last = *addr;
	while (last != 0) {
		rc++;
		last = (last << 1) & 0xff;
	}

	return (rc);
}

/*
 * Returns B_TRUE if the address in `ss' is INADDR_ANY for IPv4 or
 * :: for IPv6. Otherwise, returns B_FALSE.
 */
boolean_t
sockaddrunspec(const struct sockaddr *ss)
{
	struct sockaddr_storage data;

	switch (ss->sa_family) {
	case AF_INET:
		(void) memcpy(&data, ss, sizeof (struct sockaddr_in));
		return (((struct sockaddr_in *)&data)->sin_addr.s_addr ==
		    INADDR_ANY);
	case AF_INET6:
		(void) memcpy(&data, ss, sizeof (struct sockaddr_in6));
		return (IN6_IS_ADDR_UNSPECIFIED(
		    &((struct sockaddr_in6 *)&data)->sin6_addr));
	}

	return (B_FALSE);
}