1/*
2 * Copyright (c) 1999 - 2008, 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * inetroute.c
25 * - get a list of internet network routes
26 */
27
28/*
29 * Modification History
30 *
31 * Dieter Siegmund (dieter@apple.com) Tue Jul 14 11:33:50 PDT 1998
32 * - created
33 */
34#include <sys/param.h>
35#include <sys/socket.h>
36#include <sys/mbuf.h>
37#include <net/if.h>
38#include <net/if_dl.h>
39#include <net/if_types.h>
40#include <net/route.h>
41#include <netinet/in.h>
42#include <sys/sysctl.h>
43#include <netdb.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <sys/cdefs.h>
49#include <sys/types.h>
50#include <sys/socket.h>
51#include <arpa/inet.h> /* has inet_ntoa, etc. */
52
53#include "inetroute.h"
54#include "util.h"
55
56#define INDEX_NONE	-1
57
58static __inline__ void *
59next_sockaddr(struct sockaddr * sa)
60{
61    int		offset;
62
63    offset = (sa->sa_len != 0)
64	? roundup(sa->sa_len, sizeof(uint32_t))
65	: sizeof(uint32_t);
66    return (((caddr_t)(sa) + offset));
67}
68
69inetroute_list_t *
70inetroute_list_init()
71{
72    char *		buf = NULL;
73    char *		lim;
74    inetroute_list_t *	list_p = NULL;
75    int			list_size = 2;
76    int 		mib[6];
77    size_t 		needed;
78    char * 		next;
79    struct rt_msghdr *	rtm;
80
81    mib[0] = CTL_NET;
82    mib[1] = PF_ROUTE;
83    mib[2] = 0;
84    mib[3] = 0;
85    mib[4] = NET_RT_DUMP;
86    mib[5] = 0;
87    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
88	perror("route-sysctl-estimate");
89	goto err;
90    }
91    if ((buf = malloc(needed)) == 0) {
92	goto err;
93    }
94    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
95	perror("sysctl of routing table");
96	goto err;
97    }
98    list_p = (inetroute_list_t *)malloc(sizeof(*list_p));
99    list_p->def_index = INDEX_NONE;
100    if (list_p == NULL)
101	goto err;
102    list_p->count = 0;
103    list_p->list = (inetroute_t *)malloc(sizeof(*(list_p->list)) * list_size);
104    if (list_p->list == NULL)
105	goto err;
106    lim = buf + needed;
107    for (next = buf; next < lim; next += rtm->rtm_msglen) {
108	void *		  addrs;
109	struct sockaddr * dst = NULL;
110	struct sockaddr * gateway = NULL;
111	struct sockaddr * mask = NULL;
112
113	/* ALIGN: kernel ensures that next will be aligned, cast ok */
114	rtm = (struct rt_msghdr *)(void *)next;
115	addrs = (void *)(&rtm[1]);
116	if (rtm->rtm_addrs & RTA_DST) {
117	    dst = (struct sockaddr *)addrs;
118	    addrs = next_sockaddr(dst);
119	}
120	if (rtm->rtm_addrs & RTA_GATEWAY) {
121	    gateway = (struct sockaddr *)addrs;
122	    addrs = next_sockaddr(gateway);
123	}
124	if (rtm->rtm_addrs & RTA_NETMASK) {
125	    mask = (struct sockaddr *)addrs;
126	}
127
128	if (dst && dst->sa_family == AF_INET
129	    && gateway
130	    && mask
131	    && !(rtm->rtm_flags & RTF_HOST)) {
132	    /* ALIGN: dst aligned, after cast
133	     * dst_p is aligned to fields of dst. */
134	    struct sockaddr_in * dst_p = (struct sockaddr_in *)(void *)dst;
135
136	    /* ALIGN: mask_p aligned, after cast, mask_p aligned to fields of
137	     * mask. */
138	    struct sockaddr_in * mask_p = (struct sockaddr_in *)(void *)mask;
139
140	    inetroute_t * entry;
141	    if (list_p->count == list_size) {
142		list_size *= 2;
143		list_p->list = (inetroute_t *)
144		    realloc(list_p->list, sizeof(*(list_p->list)) * list_size);
145		if (list_p->list == NULL)
146		    goto err;
147	    }
148	    entry = list_p->list + list_p->count;
149	    bzero(entry, sizeof(*entry));
150	    entry->dest = dst_p->sin_addr;
151	    if (mask_p->sin_len != 0) {
152		entry->mask = mask_p->sin_addr;
153	    }
154	    /* remember the non-interface-scoped default route */
155	    if ((rtm->rtm_flags & RTF_IFSCOPE) == 0
156		&& dst_p->sin_addr.s_addr == INADDR_ANY) {
157		list_p->def_index = list_p->count;
158	    }
159	    if (gateway->sa_family == AF_LINK) {
160		/* ALIGN: gateway aligned.  After cast,
161		 * sdl fields should be aligned */
162		struct sockaddr_dl * sdl = (struct sockaddr_dl *)(void *)gateway;
163		entry->gateway.link = *sdl;
164	    }
165	    else {
166		struct sockaddr_in * in_p = (struct sockaddr_in *)(void *)gateway;
167		entry->gateway.inet = *in_p;
168	    }
169	    list_p->count++;
170	}
171    }
172    free(buf);
173    return (list_p);
174  err:
175    if (buf)
176	free(buf);
177    inetroute_list_free(&list_p);
178    return (NULL);
179}
180
181void
182inetroute_list_free(inetroute_list_t * * list)
183{
184    if (list != NULL && *list != NULL) {
185	if ((*list)->list)
186	    free((*list)->list);
187	(*list)->list = NULL;
188	free(*list);
189	*list = NULL;
190    }
191}
192
193struct in_addr *
194inetroute_default(inetroute_list_t * list_p)
195{
196    inetroute_t * 	entry;
197
198    if (list_p->def_index == INDEX_NONE) {
199	return (NULL);
200    }
201    entry = list_p->list + list_p->def_index;
202    if (entry->gateway.inet.sin_family == AF_INET) {
203	return (&(entry->gateway.inet.sin_addr));
204    }
205    return (NULL);
206}
207
208void
209inetroute_list_print(inetroute_list_t * list_p)
210{
211    int i;
212
213    for (i = 0; i < list_p->count; i++) {
214	inetroute_t * entry = list_p->list + i;
215
216	if (entry->gateway.link.sdl_family == AF_LINK) {
217	    printf("%s ==> link %d\n",
218		   inet_nettoa(entry->dest, entry->mask),
219		   entry->gateway.link.sdl_index);
220	}
221	else {
222	    printf("%s ==> %s\n",
223		   inet_nettoa(entry->dest, entry->mask),
224		   inet_ntoa(entry->gateway.inet.sin_addr));
225	}
226    }
227}
228
229#ifdef TEST_INETROUTE
230int
231main()
232{
233    inetroute_list_t *	list_p;
234    struct in_addr *	def;
235
236    list_p = inetroute_list_init();
237    if (list_p == NULL)
238	exit(0);
239    inetroute_list_print(list_p);
240    def = inetroute_default(list_p);
241    if (def) {
242	printf("default route: %s\n", inet_ntoa(*def));
243    }
244    inetroute_list_free(&list_p);
245    exit(0);
246}
247#endif /* TEST_INETROUTE */
248