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