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