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 *
mibget(int sd)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
free_itemlist(mib_item_t * item_list)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
get_ip_route(struct in_addr client_addr)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
find_best_server_int(char ** addr_list,char * client_name)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