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 1991-2003 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 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <errno.h> 34 35 #include <sys/types.h> 36 #include <sys/stream.h> 37 #include <sys/stropts.h> 38 #include <sys/tihdr.h> 39 #include <sys/tiuser.h> 40 #include <sys/timod.h> 41 42 #include <sys/socket.h> 43 #include <sys/sockio.h> 44 #include <netinet/in.h> 45 #include <net/if.h> 46 47 #include <inet/common.h> 48 #include <inet/mib2.h> 49 #include <inet/ip.h> 50 #include <netinet/igmp_var.h> 51 #include <netinet/ip_mroute.h> 52 53 #include <arpa/inet.h> 54 55 #include <netdb.h> 56 #include <nss_dbdefs.h> 57 #include <fcntl.h> 58 #include <stropts.h> 59 60 #include "bootparam_private.h" 61 62 typedef struct mib_item_s { 63 struct mib_item_s *next_item; 64 long group; 65 long mib_id; 66 long length; 67 char *valp; 68 } mib_item_t; 69 70 static void free_itemlist(mib_item_t *); 71 72 static mib_item_t * 73 mibget(int sd) 74 { 75 char buf[512]; 76 int flags; 77 int i, j, getcode; 78 struct strbuf ctlbuf, databuf; 79 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)(void *)buf; 80 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)(void *)buf; 81 struct T_error_ack *tea = (struct T_error_ack *)(void *)buf; 82 struct opthdr *req; 83 mib_item_t *first_item = nilp(mib_item_t); 84 mib_item_t *last_item = nilp(mib_item_t); 85 mib_item_t *temp; 86 87 tor->PRIM_type = T_SVR4_OPTMGMT_REQ; 88 tor->OPT_offset = sizeof (struct T_optmgmt_req); 89 tor->OPT_length = sizeof (struct opthdr); 90 tor->MGMT_flags = T_CURRENT; 91 req = (struct opthdr *)&tor[1]; 92 req->level = MIB2_IP; /* any MIB2_xxx value ok here */ 93 req->name = 0; 94 req->len = 0; 95 96 ctlbuf.buf = buf; 97 ctlbuf.len = tor->OPT_length + tor->OPT_offset; 98 flags = 0; 99 if (putmsg(sd, &ctlbuf, nilp(struct strbuf), flags) == -1) { 100 perror("mibget: putmsg(ctl) failed"); 101 goto error_exit; 102 } 103 /* 104 * each reply consists of a ctl part for one fixed structure 105 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, 106 * containing an opthdr structure. level/name identify the entry, 107 * len is the size of the data part of the message. 108 */ 109 req = (struct opthdr *)&toa[1]; 110 ctlbuf.maxlen = sizeof (buf); 111 for (j = 1; ; j++) { 112 flags = 0; 113 getcode = getmsg(sd, &ctlbuf, nilp(struct strbuf), &flags); 114 if (getcode == -1) { 115 perror("mibget getmsg(ctl) failed"); 116 if (debug) { 117 msgout("# level name len"); 118 i = 0; 119 for (last_item = first_item; last_item; 120 last_item = last_item->next_item) 121 msgout("%d %4ld %5ld %ld", ++i, 122 last_item->group, 123 last_item->mib_id, 124 last_item->length); 125 } 126 goto error_exit; 127 } 128 if ((getcode == 0) && 129 (ctlbuf.len >= sizeof (struct T_optmgmt_ack))&& 130 (toa->PRIM_type == T_OPTMGMT_ACK) && 131 (toa->MGMT_flags == T_SUCCESS) && 132 (req->len == 0)) { 133 if (debug) 134 msgout("mibget getmsg() %d returned EOD " 135 "(level %lu, name %lu)", 136 j, req->level, req->name); 137 return (first_item); /* this is EOD msg */ 138 } 139 140 if (ctlbuf.len >= sizeof (struct T_error_ack) && 141 tea->PRIM_type == T_ERROR_ACK) { 142 msgout("mibget %d gives T_ERROR_ACK: " 143 "TLI_error = 0x%lx, UNIX_error = 0x%lx", 144 j, tea->TLI_error, tea->UNIX_error); 145 errno = (tea->TLI_error == TSYSERR) 146 ? tea->UNIX_error : EPROTO; 147 goto error_exit; 148 } 149 150 if (getcode != MOREDATA || 151 ctlbuf.len < sizeof (struct T_optmgmt_ack) || 152 toa->PRIM_type != T_OPTMGMT_ACK || 153 toa->MGMT_flags != T_SUCCESS) { 154 msgout("mibget getmsg(ctl) %d returned %d, " 155 "ctlbuf.len = %d, PRIM_type = %ld", 156 j, getcode, ctlbuf.len, toa->PRIM_type); 157 if (toa->PRIM_type == T_OPTMGMT_ACK) 158 msgout("T_OPTMGMT_ACK: MGMT_flags = 0x%lx, " 159 "req->len = %lu", 160 toa->MGMT_flags, req->len); 161 errno = ENOMSG; 162 goto error_exit; 163 } 164 165 temp = (mib_item_t *)malloc(sizeof (mib_item_t)); 166 if (!temp) { 167 perror("mibget malloc failed"); 168 goto error_exit; 169 } 170 if (last_item) 171 last_item->next_item = temp; 172 else 173 first_item = temp; 174 last_item = temp; 175 last_item->next_item = nilp(mib_item_t); 176 last_item->group = req->level; 177 last_item->mib_id = req->name; 178 last_item->length = req->len; 179 last_item->valp = (char *)malloc(req->len); 180 if (debug) 181 msgout( 182 "msg %d: group = %4ld mib_id = %5ld length = %ld", 183 j, last_item->group, last_item->mib_id, 184 last_item->length); 185 186 databuf.maxlen = last_item->length; 187 databuf.buf = last_item->valp; 188 databuf.len = 0; 189 flags = 0; 190 getcode = getmsg(sd, nilp(struct strbuf), &databuf, &flags); 191 if (getcode == -1) { 192 perror("mibget getmsg(data) failed"); 193 goto error_exit; 194 } else if (getcode != 0) { 195 msgout("xmibget getmsg(data) returned %d, " 196 "databuf.maxlen = %d, databuf.len = %d", 197 getcode, databuf.maxlen, databuf.len); 198 goto error_exit; 199 } 200 } 201 202 error_exit: 203 free_itemlist(first_item); 204 return (NULL); 205 } 206 207 static void 208 free_itemlist(mib_item_t *item_list) 209 { 210 mib_item_t *item; 211 212 while (item_list) { 213 item = item_list; 214 item_list = item->next_item; 215 if (item->valp) 216 free(item->valp); 217 free(item); 218 } 219 } 220 221 /* 222 * If we are a router, return address of interface closest to client. 223 * If we are not a router, look through our routing table and return 224 * address of "best" router that is on same net as client. 225 * 226 * We expect the router flag to show up first, followed by interface 227 * addr group, followed by the routing table. 228 */ 229 230 in_addr_t 231 get_ip_route(struct in_addr client_addr) 232 { 233 boolean_t found; 234 mib_item_t *item_list; 235 mib_item_t *item; 236 int sd; 237 mib2_ip_t *mip; 238 mib2_ipAddrEntry_t *map; 239 mib2_ipRouteEntry_t *rp; 240 int ip_forwarding = 2; /* off */ 241 /* mask of interface used to route to client and best_router */ 242 struct in_addr interface_mask; 243 /* address of interface used to route to client and best_router */ 244 struct in_addr interface_addr; 245 /* address of "best router"; i.e. the answer */ 246 struct in_addr best_router; 247 248 interface_mask.s_addr = 0L; 249 interface_addr.s_addr = 0L; 250 best_router.s_addr = 0L; 251 252 /* open a stream to IP */ 253 sd = open("/dev/ip", O_RDWR); 254 if (sd == -1) { 255 perror("ip open"); 256 (void) close(sd); 257 msgout("can't open mib stream"); 258 return (0); 259 } 260 261 /* send down a request and suck up all the mib info from IP */ 262 if ((item_list = mibget(sd)) == nilp(mib_item_t)) { 263 msgout("mibget() failed"); 264 (void) close(sd); 265 return (0); 266 } 267 268 /* 269 * We make three passes through the list of collected IP mib 270 * information. First we figure out if we are a router. Next, 271 * we find which of our interfaces is on the same subnet as 272 * the client. Third, we paw through our own routing table 273 * looking for a useful router address. 274 */ 275 276 /* 277 * The general IP group. 278 */ 279 for (item = item_list; item; item = item->next_item) { 280 if ((item->group == MIB2_IP) && (item->mib_id == 0)) { 281 /* are we an IP router? */ 282 mip = (mib2_ip_t *)(void *)item->valp; 283 ip_forwarding = mip->ipForwarding; 284 break; 285 } 286 } 287 288 /* 289 * The interface group. 290 */ 291 for (item = item_list, found = B_FALSE; item != NULL && !found; 292 item = item->next_item) { 293 if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) { 294 /* 295 * Try to find out which interface is up, configured, 296 * not loopback, and on the same subnet as the client. 297 * Save its address and netmask. 298 */ 299 map = (mib2_ipAddrEntry_t *)(void *)item->valp; 300 while ((char *)map < item->valp + item->length) { 301 in_addr_t addr, mask, net; 302 int ifflags; 303 304 ifflags = map->ipAdEntInfo.ae_flags; 305 addr = map->ipAdEntAddr; 306 mask = map->ipAdEntNetMask; 307 net = addr & mask; 308 309 if ((ifflags & IFF_LOOPBACK | IFF_UP) == 310 IFF_UP && addr != INADDR_ANY && 311 net == (client_addr.s_addr & mask)) { 312 interface_addr.s_addr = addr; 313 interface_mask.s_addr = mask; 314 found = B_TRUE; 315 break; 316 } 317 map++; 318 } 319 } 320 } 321 322 /* 323 * If this exercise found no interface on the same subnet as 324 * the client, then we can't suggest any router address to 325 * use. 326 */ 327 if (interface_addr.s_addr == 0) { 328 if (debug) 329 msgout("get_ip_route: no interface on same net " 330 "as client"); 331 (void) close(sd); 332 free_itemlist(item_list); 333 return (0); 334 } 335 336 /* 337 * If we are a router, we return to client the address of our 338 * interface on the same net as the client. 339 */ 340 if (ip_forwarding == 1) { 341 if (debug) 342 msgout("get_ip_route: returning local addr %s", 343 inet_ntoa(interface_addr)); 344 (void) close(sd); 345 free_itemlist(item_list); 346 return (interface_addr.s_addr); 347 } 348 349 if (debug) { 350 msgout("interface_addr = %s.", inet_ntoa(interface_addr)); 351 msgout("interface_mask = %s", inet_ntoa(interface_mask)); 352 } 353 354 355 /* 356 * The routing table group. 357 */ 358 for (item = item_list; item; item = item->next_item) { 359 if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_21)) { 360 if (debug) 361 msgout("%lu records for ipRouteEntryTable", 362 item->length / 363 sizeof (mib2_ipRouteEntry_t)); 364 365 for (rp = (mib2_ipRouteEntry_t *)(void *)item->valp; 366 (char *)rp < item->valp + item->length; 367 rp++) { 368 if (debug >= 2) 369 msgout("ire_type = %d, next_hop = 0x%x", 370 rp->ipRouteInfo.re_ire_type, 371 rp->ipRouteNextHop); 372 373 /* 374 * We are only interested in real 375 * gateway routes. 376 */ 377 if ((rp->ipRouteInfo.re_ire_type != 378 IRE_DEFAULT) && 379 (rp->ipRouteInfo.re_ire_type != 380 IRE_PREFIX) && 381 (rp->ipRouteInfo.re_ire_type != 382 IRE_HOST) && 383 (rp->ipRouteInfo.re_ire_type != 384 IRE_HOST_REDIRECT)) 385 continue; 386 387 /* 388 * We are only interested in routes with 389 * a next hop on the same subnet as 390 * the client. 391 */ 392 if ((rp->ipRouteNextHop & 393 interface_mask.s_addr) != 394 (interface_addr.s_addr & 395 interface_mask.s_addr)) 396 continue; 397 398 /* 399 * We have a valid route. Give preference 400 * to default routes. 401 */ 402 if ((rp->ipRouteDest == 0) || 403 (best_router.s_addr == 0)) 404 best_router.s_addr = 405 rp->ipRouteNextHop; 406 } 407 } 408 } 409 410 if (debug && (best_router.s_addr == 0)) 411 msgout("get_ip_route: no route found for client"); 412 413 (void) close(sd); 414 free_itemlist(item_list); 415 return (best_router.s_addr); 416 } 417 418 /* 419 * Return address of server interface closest to client. 420 * 421 * If the server has only a single IP address return it. Otherwise check 422 * if the server has an interface on the same subnet as the client and 423 * return the address of that interface. 424 */ 425 426 in_addr_t 427 find_best_server_int(char **addr_list, char *client_name) 428 { 429 in_addr_t server_addr = 0; 430 struct hostent h, *hp; 431 char hbuf[NSS_BUFLEN_HOSTS]; 432 int err; 433 struct in_addr client_addr; 434 mib_item_t *item_list; 435 mib_item_t *item; 436 int sd; 437 mib2_ipAddrEntry_t *map; 438 in_addr_t client_net = 0, client_mask = 0; 439 boolean_t found_client_int; 440 441 (void) memcpy(&server_addr, addr_list[0], sizeof (in_addr_t)); 442 if (addr_list[1] == NULL) 443 return (server_addr); 444 445 hp = gethostbyname_r(client_name, &h, hbuf, sizeof (hbuf), &err); 446 if (hp == NULL) 447 return (server_addr); 448 (void) memcpy(&client_addr, hp->h_addr_list[0], sizeof (client_addr)); 449 450 /* open a stream to IP */ 451 sd = open("/dev/ip", O_RDWR); 452 if (sd == -1) { 453 perror("ip open"); 454 (void) close(sd); 455 msgout("can't open mib stream"); 456 return (server_addr); 457 } 458 459 /* send down a request and suck up all the mib info from IP */ 460 if ((item_list = mibget(sd)) == nilp(mib_item_t)) { 461 msgout("mibget() failed"); 462 (void) close(sd); 463 return (server_addr); 464 } 465 (void) close(sd); 466 467 /* 468 * Search through the list for our interface which is on the same 469 * subnet as the client and get the netmask. 470 */ 471 for (item = item_list, found_client_int = B_FALSE; 472 item != NULL && !found_client_int; item = item->next_item) { 473 if ((item->group == MIB2_IP) && (item->mib_id == MIB2_IP_20)) { 474 /* 475 * Try to find out which interface is up, configured, 476 * not loopback, and on the same subnet as the client. 477 * Save its address and netmask. 478 */ 479 map = (mib2_ipAddrEntry_t *)(void *)item->valp; 480 while ((char *)map < item->valp + item->length) { 481 in_addr_t addr, mask, net; 482 int ifflags; 483 484 ifflags = map->ipAdEntInfo.ae_flags; 485 addr = map->ipAdEntAddr; 486 mask = map->ipAdEntNetMask; 487 net = addr & mask; 488 489 if ((ifflags & IFF_LOOPBACK|IFF_UP) == IFF_UP && 490 addr != INADDR_ANY && 491 (client_addr.s_addr & mask) == net) { 492 client_net = net; 493 client_mask = mask; 494 found_client_int = B_TRUE; 495 break; 496 } 497 map++; 498 } 499 } 500 } 501 502 /* 503 * If we found the interface check which is the best IP address. 504 */ 505 if (found_client_int) { 506 while (*addr_list != NULL) { 507 in_addr_t addr; 508 509 (void) memcpy(&addr, *addr_list, sizeof (in_addr_t)); 510 if ((addr & client_mask) == client_net) { 511 server_addr = addr; 512 break; 513 } 514 addr_list++; 515 } 516 } 517 518 if (debug && server_addr == 0) 519 msgout("No usable interface for returning reply"); 520 521 free_itemlist(item_list); 522 return (server_addr); 523 } 524