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
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.
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]
23 * Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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>
42 #include <sys/socket.h>
43 #include <sys/sockio.h>
44 #include <netinet/in.h>
47 #include <inet/common.h>
48 #include <inet/mib2.h>
50 #include <netinet/igmp_var.h>
51 #include <netinet/ip_mroute.h>
53 #include <arpa/inet.h>
56 #include <nss_dbdefs.h>
60 #include "bootparam_private.h"
62 typedef struct mib_item_s
{
63 struct mib_item_s
*next_item
;
70 static void free_itemlist(mib_item_t
*);
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
;
83 mib_item_t
*first_item
= nilp(mib_item_t
);
84 mib_item_t
*last_item
= nilp(mib_item_t
);
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 */
97 ctlbuf
.len
= tor
->OPT_length
+ tor
->OPT_offset
;
99 if (putmsg(sd
, &ctlbuf
, nilp(struct strbuf
), flags
) == -1) {
100 perror("mibget: putmsg(ctl) failed");
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.
109 req
= (struct opthdr
*)&toa
[1];
110 ctlbuf
.maxlen
= sizeof (buf
);
113 getcode
= getmsg(sd
, &ctlbuf
, nilp(struct strbuf
), &flags
);
115 perror("mibget getmsg(ctl) failed");
117 msgout("# level name len");
119 for (last_item
= first_item
; last_item
;
120 last_item
= last_item
->next_item
)
121 msgout("%d %4ld %5ld %ld", ++i
,
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
) &&
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 */
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
;
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, "
160 toa
->MGMT_flags
, req
->len
);
165 temp
= (mib_item_t
*)malloc(sizeof (mib_item_t
));
167 perror("mibget malloc failed");
171 last_item
->next_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
);
182 "msg %d: group = %4ld mib_id = %5ld length = %ld",
183 j
, last_item
->group
, last_item
->mib_id
,
186 databuf
.maxlen
= last_item
->length
;
187 databuf
.buf
= last_item
->valp
;
190 getcode
= getmsg(sd
, nilp(struct strbuf
), &databuf
, &flags
);
192 perror("mibget getmsg(data) failed");
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
);
203 free_itemlist(first_item
);
208 free_itemlist(mib_item_t
*item_list
)
214 item_list
= item
->next_item
;
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.
226 * We expect the router flag to show up first, followed by interface
227 * addr group, followed by the routing table.
231 get_ip_route(struct in_addr client_addr
)
234 mib_item_t
*item_list
;
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
;
248 interface_mask
.s_addr
= 0L;
249 interface_addr
.s_addr
= 0L;
250 best_router
.s_addr
= 0L;
252 /* open a stream to IP */
253 sd
= open("/dev/ip", O_RDWR
);
257 msgout("can't open mib stream");
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");
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.
277 * The general IP group.
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
;
289 * The interface group.
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
)) {
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.
299 map
= (mib2_ipAddrEntry_t
*)(void *)item
->valp
;
300 while ((char *)map
< item
->valp
+ item
->length
) {
301 in_addr_t addr
, mask
, net
;
304 ifflags
= map
->ipAdEntInfo
.ae_flags
;
305 addr
= map
->ipAdEntAddr
;
306 mask
= map
->ipAdEntNetMask
;
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
;
323 * If this exercise found no interface on the same subnet as
324 * the client, then we can't suggest any router address to
327 if (interface_addr
.s_addr
== 0) {
329 msgout("get_ip_route: no interface on same net "
332 free_itemlist(item_list
);
337 * If we are a router, we return to client the address of our
338 * interface on the same net as the client.
340 if (ip_forwarding
== 1) {
342 msgout("get_ip_route: returning local addr %s",
343 inet_ntoa(interface_addr
));
345 free_itemlist(item_list
);
346 return (interface_addr
.s_addr
);
350 msgout("interface_addr = %s.", inet_ntoa(interface_addr
));
351 msgout("interface_mask = %s", inet_ntoa(interface_mask
));
356 * The routing table group.
358 for (item
= item_list
; item
; item
= item
->next_item
) {
359 if ((item
->group
== MIB2_IP
) && (item
->mib_id
== MIB2_IP_21
)) {
361 msgout("%lu records for ipRouteEntryTable",
363 sizeof (mib2_ipRouteEntry_t
));
365 for (rp
= (mib2_ipRouteEntry_t
*)(void *)item
->valp
;
366 (char *)rp
< item
->valp
+ item
->length
;
369 msgout("ire_type = %d, next_hop = 0x%x",
370 rp
->ipRouteInfo
.re_ire_type
,
374 * We are only interested in real
377 if ((rp
->ipRouteInfo
.re_ire_type
!=
379 (rp
->ipRouteInfo
.re_ire_type
!=
381 (rp
->ipRouteInfo
.re_ire_type
!=
383 (rp
->ipRouteInfo
.re_ire_type
!=
388 * We are only interested in routes with
389 * a next hop on the same subnet as
392 if ((rp
->ipRouteNextHop
&
393 interface_mask
.s_addr
) !=
394 (interface_addr
.s_addr
&
395 interface_mask
.s_addr
))
399 * We have a valid route. Give preference
402 if ((rp
->ipRouteDest
== 0) ||
403 (best_router
.s_addr
== 0))
410 if (debug
&& (best_router
.s_addr
== 0))
411 msgout("get_ip_route: no route found for client");
414 free_itemlist(item_list
);
415 return (best_router
.s_addr
);
419 * Return address of server interface closest to client.
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.
427 find_best_server_int(char **addr_list
, char *client_name
)
429 in_addr_t server_addr
= 0;
430 struct hostent h
, *hp
;
431 char hbuf
[NSS_BUFLEN_HOSTS
];
433 struct in_addr client_addr
;
434 mib_item_t
*item_list
;
437 mib2_ipAddrEntry_t
*map
;
438 in_addr_t client_net
= 0, client_mask
= 0;
439 boolean_t found_client_int
;
441 (void) memcpy(&server_addr
, addr_list
[0], sizeof (in_addr_t
));
442 if (addr_list
[1] == NULL
)
443 return (server_addr
);
445 hp
= gethostbyname_r(client_name
, &h
, hbuf
, sizeof (hbuf
), &err
);
447 return (server_addr
);
448 (void) memcpy(&client_addr
, hp
->h_addr_list
[0], sizeof (client_addr
));
450 /* open a stream to IP */
451 sd
= open("/dev/ip", O_RDWR
);
455 msgout("can't open mib stream");
456 return (server_addr
);
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");
463 return (server_addr
);
468 * Search through the list for our interface which is on the same
469 * subnet as the client and get the netmask.
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
)) {
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.
479 map
= (mib2_ipAddrEntry_t
*)(void *)item
->valp
;
480 while ((char *)map
< item
->valp
+ item
->length
) {
481 in_addr_t addr
, mask
, net
;
484 ifflags
= map
->ipAdEntInfo
.ae_flags
;
485 addr
= map
->ipAdEntAddr
;
486 mask
= map
->ipAdEntNetMask
;
489 if ((ifflags
& IFF_LOOPBACK
|IFF_UP
) == IFF_UP
&&
490 addr
!= INADDR_ANY
&&
491 (client_addr
.s_addr
& mask
) == net
) {
494 found_client_int
= B_TRUE
;
503 * If we found the interface check which is the best IP address.
505 if (found_client_int
) {
506 while (*addr_list
!= NULL
) {
509 (void) memcpy(&addr
, *addr_list
, sizeof (in_addr_t
));
510 if ((addr
& client_mask
) == client_net
) {
518 if (debug
&& server_addr
== 0)
519 msgout("No usable interface for returning reply");
521 free_itemlist(item_list
);
522 return (server_addr
);