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