1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <unistd.h>
27 #include <netinet/in.h>
28 #include <libinetutil.h>
29 #include <inet/ip.h>
30 #include <strings.h>
31 #include <stddef.h>
32 #include <errno.h>
33 #include <libsocket_priv.h>
34
35 /*
36 * Internet utility functions.
37 */
38
39 /*
40 * Given a host-order address, calculate client's default net mask.
41 * Consult netmasks database to see if net is further subnetted.
42 * We'll only snag the first netmask that matches our criteria.
43 * We return the resultant netmask in host order.
44 */
45 void
get_netmask4(const struct in_addr * n_addrp,struct in_addr * s_addrp)46 get_netmask4(const struct in_addr *n_addrp, struct in_addr *s_addrp)
47 {
48 struct in_addr hp, tp;
49
50 /*
51 * First check if VLSM is in use.
52 */
53 hp.s_addr = htonl(n_addrp->s_addr);
54 if (getnetmaskbyaddr(hp, &tp) == 0) {
55 s_addrp->s_addr = ntohl(tp.s_addr);
56 return;
57 }
58
59 /*
60 * Fall back on standard classed networks.
61 */
62 if (IN_CLASSA(n_addrp->s_addr))
63 s_addrp->s_addr = IN_CLASSA_NET;
64 else if (IN_CLASSB(n_addrp->s_addr))
65 s_addrp->s_addr = IN_CLASSB_NET;
66 else if (IN_CLASSC(n_addrp->s_addr))
67 s_addrp->s_addr = IN_CLASSC_NET;
68 else
69 s_addrp->s_addr = IN_CLASSE_NET;
70 }
71
72 /*
73 * Checks if the IP addresses `ssp1' and `ssp2' are equal.
74 */
75 boolean_t
sockaddrcmp(const struct sockaddr_storage * ssp1,const struct sockaddr_storage * ssp2)76 sockaddrcmp(const struct sockaddr_storage *ssp1,
77 const struct sockaddr_storage *ssp2)
78 {
79 struct in_addr addr1, addr2;
80 const struct in6_addr *addr6p1, *addr6p2;
81
82 if (ssp1->ss_family != ssp2->ss_family)
83 return (B_FALSE);
84
85 if (ssp1 == ssp2)
86 return (B_TRUE);
87
88 switch (ssp1->ss_family) {
89 case AF_INET:
90 addr1 = ((const struct sockaddr_in *)ssp1)->sin_addr;
91 addr2 = ((const struct sockaddr_in *)ssp2)->sin_addr;
92 return (addr1.s_addr == addr2.s_addr);
93 case AF_INET6:
94 addr6p1 = &((const struct sockaddr_in6 *)ssp1)->sin6_addr;
95 addr6p2 = &((const struct sockaddr_in6 *)ssp2)->sin6_addr;
96 return (IN6_ARE_ADDR_EQUAL(addr6p1, addr6p2));
97 }
98 return (B_FALSE);
99 }
100
101 /*
102 * Stores the netmask in `mask' for the given prefixlen `plen' and also sets
103 * `sa_family' in `mask'. Because this function does not require aligned
104 * access to the data inside of the sockaddr_in/6 structures, the code can
105 * use offsetof() to find the right place in the incoming structure. Why is
106 * using that beneficial? Less issues with lint. When using a direct cast
107 * of the struct sockaddr_storage structure to sockaddr_in6, a lint warning
108 * is generated because the former is composed of 16bit & 8bit elements whilst
109 * sockaddr_in6 has a 32bit alignment requirement.
110 */
111 int
plen2mask(uint_t prefixlen,sa_family_t af,struct sockaddr * mask)112 plen2mask(uint_t prefixlen, sa_family_t af, struct sockaddr *mask)
113 {
114 uint8_t *addr;
115
116 if (af == AF_INET) {
117 if (prefixlen > IP_ABITS)
118 return (EINVAL);
119 bzero(mask, sizeof (struct sockaddr_in));
120 addr = (uint8_t *)mask;
121 addr += offsetof(struct sockaddr_in, sin_addr);
122 } else {
123 if (prefixlen > IPV6_ABITS)
124 return (EINVAL);
125 bzero(mask, sizeof (struct sockaddr_in6));
126 addr = (uint8_t *)mask;
127 addr += offsetof(struct sockaddr_in6, sin6_addr);
128 }
129 mask->sa_family = af;
130
131 while (prefixlen > 0) {
132 if (prefixlen >= 8) {
133 *addr++ = 0xFF;
134 prefixlen -= 8;
135 continue;
136 }
137 *addr |= 1 << (8 - prefixlen);
138 prefixlen--;
139 }
140 return (0);
141 }
142
143 /*
144 * Convert a mask to a prefix length.
145 * Returns prefix length on success, -1 otherwise.
146 * The comments (above) for plen2mask about the use of `mask' also apply
147 * to this function and the choice to use offsetof here too.
148 */
149 int
mask2plen(const struct sockaddr * mask)150 mask2plen(const struct sockaddr *mask)
151 {
152 int rc = 0;
153 uint8_t last;
154 uint8_t *addr;
155 int limit;
156
157 if (mask->sa_family == AF_INET) {
158 limit = IP_ABITS;
159 addr = (uint8_t *)mask;
160 addr += offsetof(struct sockaddr_in, sin_addr);
161 } else {
162 limit = IPV6_ABITS;
163 addr = (uint8_t *)mask;
164 addr += offsetof(struct sockaddr_in6, sin6_addr);
165 }
166
167 while (*addr == 0xff) {
168 rc += 8;
169 if (rc == limit)
170 return (limit);
171 addr++;
172 }
173
174 last = *addr;
175 while (last != 0) {
176 rc++;
177 last = (last << 1) & 0xff;
178 }
179
180 return (rc);
181 }
182
183 /*
184 * Returns B_TRUE if the address in `ss' is INADDR_ANY for IPv4 or
185 * :: for IPv6. Otherwise, returns B_FALSE.
186 */
187 boolean_t
sockaddrunspec(const struct sockaddr * ss)188 sockaddrunspec(const struct sockaddr *ss)
189 {
190 struct sockaddr_storage data;
191
192 switch (ss->sa_family) {
193 case AF_INET:
194 (void) memcpy(&data, ss, sizeof (struct sockaddr_in));
195 return (((struct sockaddr_in *)&data)->sin_addr.s_addr ==
196 INADDR_ANY);
197 case AF_INET6:
198 (void) memcpy(&data, ss, sizeof (struct sockaddr_in6));
199 return (IN6_IS_ADDR_UNSPECIFIED(
200 &((struct sockaddr_in6 *)&data)->sin6_addr));
201 }
202
203 return (B_FALSE);
204 }
205