xref: /illumos-gate/usr/src/cmd/rpcsvc/rpc.bootparamd/bootparam_ip_route.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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