xref: /illumos-gate/usr/src/lib/libsocket/inet/netmasks.c (revision 9a016c63ca347047a236dff12f0da83aac8981d1)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * All routines necessary to deal the "netmasks" database.  The sources
31  * contain mappings between 32 bit Internet addresses and corresponding
32  * 32 bit Internet address masks. The addresses are in dotted internet
33  * address notation.
34  */
35 
36 #include <stdio.h>
37 #include <ctype.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <nss_dbdefs.h>
46 
47 static int str2addr(const char *, int, void *, char *, int);
48 
49 static DEFINE_NSS_DB_ROOT(db_root);
50 
51 static void
52 _nss_initf_netmasks(nss_db_params_t *p)
53 {
54 	p->name = NSS_DBNAM_NETMASKS;
55 	p->default_config = NSS_DEFCONF_NETMASKS;
56 }
57 
58 /*
59  * Print a network number such as 129.144 as well as an IP address.
60  * Assumes network byte order for both IP addresses and network numbers
61  * (Network numbers are normally passed around in host byte order).
62  */
63 static char *
64 inet_nettoa(struct in_addr in)
65 {
66 	uint32_t addr = in.s_addr;
67 	uchar_t *up = (uchar_t *)&addr;
68 	static char result[256];
69 
70 	/* Omit leading zeros */
71 	if (up[0]) {
72 		(void) sprintf(result, "%d.%d.%d.%d",
73 		    up[0], up[1], up[2], up[3]);
74 	} else if (up[1]) {
75 		(void) sprintf(result, "%d.%d.%d", up[1], up[2], up[3]);
76 	} else if (up[2]) {
77 		(void) sprintf(result, "%d.%d", up[2], up[3]);
78 	} else {
79 		(void) sprintf(result, "%d", up[3]);
80 	}
81 	return (result);
82 }
83 
84 /*
85  * Given a 32 bit key look it up in the netmasks database
86  * based on the "netmasks" policy in /etc/nsswitch.conf.
87  * If the key is a network number with the trailing zero's removed
88  * (e.g. "192.9.200") this routine can't use inet_ntoa to convert
89  * the address to the string key.
90  * Returns zero if successful, non-zero otherwise.
91  */
92 static int
93 getnetmaskbykey(const struct in_addr addr, struct in_addr *mask)
94 {
95 	nss_XbyY_args_t arg;
96 	nss_status_t	res;
97 	char		tmp[NSS_LINELEN_NETMASKS];
98 
99 	/*
100 	 * let the backend do the allocation to store stuff for parsing.
101 	 * To simplify things, we put the dotted internet address form of
102 	 * the network address in the 'name' field as a filter to speed
103 	 * up the lookup.
104 	 */
105 	NSS_XbyY_INIT(&arg, mask, NULL, 0, str2addr);
106 	(void) strcpy(tmp, inet_nettoa(addr));
107 	arg.key.name = tmp;
108 	res = nss_search(&db_root, _nss_initf_netmasks,
109 			NSS_DBOP_NETMASKS_BYNET, &arg);
110 	(void) NSS_XbyY_FINI(&arg);
111 	return (arg.status = res);
112 }
113 
114 /*
115  * Given a 32 bit internet network number, it finds the corresponding netmask
116  * address based on the "netmasks" policy in /etc/nsswitch.conf.
117  * Returns zero if successful, non-zero otherwise.
118  * Check both for the (masked) network number and the shifted network
119  * number (e.g., both "10.0.0.0" and "10").
120  * Assumes that the caller passes in an unshifted number (or an IP address).
121  */
122 int
123 getnetmaskbynet(const struct in_addr net, struct in_addr *mask)
124 {
125 	struct in_addr net1, net2;
126 	uint32_t i;
127 
128 	i = ntohl(net.s_addr);
129 
130 	/*
131 	 * Try looking for the network number both with and without
132 	 * the trailing zeros.
133 	 */
134 	if ((i & IN_CLASSA_NET) == 0) {
135 		/* Assume already a right-shifted network number */
136 		net2.s_addr = htonl(i);
137 		if ((i & IN_CLASSB_NET) != 0) {
138 			net1.s_addr = htonl(i << IN_CLASSC_NSHIFT);
139 		} else if ((i & IN_CLASSC_NET) != 0) {
140 			net1.s_addr = htonl(i << IN_CLASSB_NSHIFT);
141 		} else {
142 			net1.s_addr = htonl(i << IN_CLASSA_NSHIFT);
143 		}
144 	} else if (IN_CLASSA(i)) {
145 		net1.s_addr = htonl(i & IN_CLASSA_NET);
146 		net2.s_addr = htonl(i >> IN_CLASSA_NSHIFT);
147 	} else if (IN_CLASSB(i)) {
148 		net1.s_addr = htonl(i & IN_CLASSB_NET);
149 		net2.s_addr = htonl(i >> IN_CLASSB_NSHIFT);
150 	} else {
151 		net1.s_addr = htonl(i & IN_CLASSC_NET);
152 		net2.s_addr = htonl(i >> IN_CLASSC_NSHIFT);
153 	}
154 
155 	if (getnetmaskbykey(net1, mask) == 0) {
156 		return (0);
157 	}
158 	if (getnetmaskbykey(net2, mask) == 0) {
159 		return (0);
160 	}
161 	return (-1);
162 }
163 
164 /*
165  * Find the netmask used for an IP address.
166  * Returns zero if successful, non-zero otherwise.
167  *
168  * Support Variable Length Subnetmasks by looking for the longest
169  * matching subnetmask in the database.
170  * Start by looking for a match for the full IP address and
171  * mask off one rightmost bit after another until we find a match.
172  * Note that for a match the found netmask must match what was used
173  * for the lookup masking.
174  * As a fallback for compatibility finally lookup the network
175  * number with and without the trailing zeros.
176  * In order to suppress redundant lookups in the name service
177  * we keep the previous lookup key and compare against it before
178  * doing the lookup.
179  */
180 int
181 getnetmaskbyaddr(const struct in_addr addr, struct in_addr *mask)
182 {
183 	struct in_addr prevnet, net;
184 	uint32_t i, maskoff;
185 
186 	i = ntohl(addr.s_addr);
187 	prevnet.s_addr = 0;
188 	mask->s_addr = 0;
189 
190 	for (maskoff = 0xFFFFFFFF; maskoff != 0; maskoff = maskoff << 1) {
191 		net.s_addr = htonl(i & maskoff);
192 
193 		if (net.s_addr != prevnet.s_addr) {
194 			if (getnetmaskbykey(net, mask) != 0) {
195 				mask->s_addr = 0;
196 			}
197 		}
198 		if (htonl(maskoff) == mask->s_addr)
199 			return (0);
200 
201 		prevnet.s_addr = net.s_addr;
202 	}
203 
204 	/*
205 	 * Non-VLSM fallback.
206 	 * Try looking for the network number with and without the trailing
207 	 * zeros.
208 	 */
209 	return (getnetmaskbynet(addr, mask));
210 }
211 
212 /*
213  * Parse netmasks entry into its components. The network address is placed
214  * in buffer for use by check_addr for 'files' backend, to match the network
215  * address. The network address is placed in the buffer as a network order
216  * internet address, if buffer is non null. The network order form of the mask
217  * itself is placed in 'ent'.
218  */
219 int
220 str2addr(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
221 {
222 	int	retval;
223 	struct in_addr	*mask = (struct in_addr *)ent;
224 	const char	*p, *limit, *start;
225 	struct in_addr	addr;
226 	int		i;
227 	char		tmp[NSS_LINELEN_NETMASKS];
228 
229 	p = instr;
230 	limit = p + lenstr;
231 	retval = NSS_STR_PARSE_PARSE;
232 
233 	while (p < limit && isspace(*p))	/* skip leading whitespace */
234 		p++;
235 
236 	if (buffer) {	/* for 'files' backend verification */
237 		for (start = p, i = 0; p < limit && !isspace(*p); p++)
238 			i++;
239 		if (p < limit && i < buflen) {
240 			(void) memcpy(tmp, start, i);
241 			tmp[i] = '\0';
242 			addr.s_addr = inet_addr(tmp);
243 			/* Addr will always be an ipv4 address (32bits) */
244 			if (addr.s_addr == 0xffffffffUL)
245 				return (NSS_STR_PARSE_PARSE);
246 			else {
247 				(void) memcpy(buffer, (char *)&addr,
248 				    sizeof (struct in_addr));
249 			}
250 		} else
251 			return (NSS_STR_PARSE_ERANGE);
252 	}
253 
254 	while (p < limit && isspace(*p))	/* skip intermediate */
255 		p++;
256 
257 	if (mask) {
258 		for (start = p, i = 0; p < limit && !isspace(*p); p++)
259 			i++;
260 		if (p <= limit) {
261 			if ((i + 1) > NSS_LINELEN_NETMASKS)
262 				return (NSS_STR_PARSE_ERANGE);
263 			(void) memcpy(tmp, start, i);
264 			tmp[i] = '\0';
265 			addr.s_addr = inet_addr(tmp);
266 			/* Addr will always be an ipv4 address (32bits) */
267 			if (addr.s_addr == 0xffffffffUL)
268 				retval = NSS_STR_PARSE_PARSE;
269 			else {
270 				mask->s_addr = addr.s_addr;
271 				retval = NSS_STR_PARSE_SUCCESS;
272 			}
273 		}
274 	}
275 
276 	return (retval);
277 }
278