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