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