1/*
2 * Copyright (c) 2000-2013 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/*
25 * rtutil.c
26 * - routing table routines
27 */
28
29/*
30 * Modification History
31 *
32 * June 23, 2009	Dieter Siegmund (dieter@apple.com)
33 * - split out from ipconfigd.c
34 */
35
36
37#include "rtutil.h"
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <string.h>
43#include <syslog.h>
44#include <sys/types.h>
45#include <sys/param.h>
46#include <sys/sysctl.h>
47#include <sys/errno.h>
48#include <sys/socket.h>
49#include <net/route.h>
50#include <net/if_dl.h>
51#define KERNEL_PRIVATE
52#include <sys/ioctl.h>
53#undef KERNEL_PRIVATE
54#include <mach/boolean.h>
55#include "symbol_scope.h"
56#include "util.h"
57#include "arp.h"
58#include "mylog.h"
59#include "globals.h"
60
61STATIC void
62set_sockaddr_in(struct sockaddr_in * sin_p, struct in_addr iaddr)
63{
64    sin_p->sin_len = sizeof(*sin_p);
65    sin_p->sin_family = AF_INET;
66    sin_p->sin_addr = iaddr;
67}
68
69PRIVATE_EXTERN boolean_t
70host_route(int cmd, struct in_addr iaddr)
71{
72    int 			len;
73    struct in_addr		loopback = { htonl(INADDR_LOOPBACK) };
74    boolean_t			ret = TRUE;
75    struct rt_msghdr *		rtm;
76    struct {
77	struct rt_msghdr	hdr;
78	struct sockaddr_in	dst;
79	struct sockaddr_in	gway;
80    } 				rtmsg;
81    int 			sockfd = -1;
82
83    sockfd = arp_open_routing_socket();
84    if (sockfd < 0) {
85	my_log(LOG_NOTICE, "host_route: open routing socket failed, %s",
86	       strerror(errno));
87	ret = FALSE;
88	goto done;
89    }
90
91    memset(&rtmsg, 0, sizeof(rtmsg));
92    rtm = &rtmsg.hdr;
93    rtm->rtm_type = cmd;
94    rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_HOST;
95    rtm->rtm_version = RTM_VERSION;
96    rtm->rtm_seq = arp_get_next_seq();
97    rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
98    set_sockaddr_in(&rtmsg.dst, iaddr);
99    set_sockaddr_in(&rtmsg.gway, loopback);
100
101    len = sizeof(rtmsg);
102    rtm->rtm_msglen = len;
103    if (write(sockfd, &rtmsg, len) < 0) {
104	int	error = errno;
105
106	switch (error) {
107	case ESRCH:
108	case EEXIST:
109	    my_log(LOG_DEBUG, "host_route: write routing socket failed, %s",
110		   strerror(error));
111	    break;
112	default:
113	    my_log(LOG_NOTICE, "host_route: write routing socket failed, %s",
114		   strerror(error));
115	    break;
116	}
117	ret = FALSE;
118    }
119 done:
120    if (sockfd >= 0) {
121	close(sockfd);
122    }
123    return (ret);
124}
125
126#define ROUNDUP(a) \
127    ((a) > 0 ? (1 + (((a) - 1) | (sizeof(u_int32_t) - 1))) : sizeof(u_int32_t))
128
129PRIVATE_EXTERN int
130rt_xaddrs(const char * cp, const char * cplim, struct rt_addrinfo * rtinfo)
131{
132    int 		i;
133    struct sockaddr *	sa;
134
135    bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
136    for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
137	if ((rtinfo->rti_addrs & (1 << i)) == 0) {
138	    continue;
139	}
140	sa = (struct sockaddr *)cp;
141	if ((cp + sa->sa_len) > cplim) {
142	    return (EINVAL);
143	}
144	rtinfo->rti_info[i] = sa;
145	cp += ROUNDUP(sa->sa_len);
146    }
147    return (0);
148}
149
150/*
151 * Function: subnet_route_if_index
152 * Purpose:
153 *   Look up the subnet route for the given subnet.
154 * Returns:
155 *   Non-zero interface index if the route was found, zero otherwise.
156 *
157 */
158PRIVATE_EXTERN int
159subnet_route_if_index(struct in_addr netaddr, struct in_addr netmask)
160{
161    struct sockaddr_in *	addr_p;
162    int 			len;
163    int				n;
164    void *			ptr;
165    int				pid;
166    boolean_t			ret_if_index = 0;
167    struct rt_msghdr *		rtm;
168    int 			rtm_seq;
169    struct {
170	struct rt_msghdr hdr;
171	char		 data[512];
172    }				rtmsg;
173    struct sockaddr_dl *	sdl;
174    int 			sockfd = -1;
175
176    sockfd = arp_open_routing_socket();
177    if (sockfd < 0) {
178	my_log(LOG_NOTICE,
179	       "subnet_route_if_index: open routing socket failed, %s",
180	       strerror(errno));
181	goto done;
182    }
183    memset(&rtmsg, 0, sizeof(rtmsg));
184    rtm = &rtmsg.hdr;
185    rtm->rtm_type = RTM_GET_SILENT;
186    rtm->rtm_version = RTM_VERSION;
187    rtm->rtm_seq = rtm_seq = arp_get_next_seq();
188    rtm->rtm_addrs = RTA_DST | RTA_NETMASK;
189    ptr = rtmsg.data;
190
191    /* RTA_DST */
192    addr_p = (struct sockaddr_in *)ptr;
193    addr_p->sin_len = sizeof(*addr_p);
194    addr_p->sin_family = AF_INET;
195    addr_p->sin_addr = netaddr;
196    ptr += sizeof(*addr_p);
197
198    /* RTA_NETMASK */
199    addr_p = (struct sockaddr_in *)ptr;
200    addr_p->sin_len = sizeof(*addr_p);
201    addr_p->sin_family = AF_INET;
202    addr_p->sin_addr = netmask;
203    ptr += sizeof(*addr_p);
204
205    len = (char *)ptr - (char *)&rtmsg;
206    rtm->rtm_msglen = len;
207    if (write(sockfd, &rtmsg, len) < 0) {
208	int	error = errno;
209
210	switch (error) {
211	case ESRCH:
212	case EEXIST:
213	    my_log(LOG_DEBUG,
214		   "subnet_route_if_index: write routing socket failed, %s",
215		   strerror(error));
216	    break;
217	default:
218	    my_log(LOG_NOTICE,
219		   "subnet_route_if_index: write routing socket failed, %s",
220		   strerror(error));
221	    break;
222	}
223	goto done;
224    }
225    pid = getpid();
226    while ((n = read(sockfd, &rtmsg, sizeof(rtmsg))) > 0) {
227	struct rt_addrinfo	info;
228
229	if (rtm->rtm_type != RTM_GET
230	    || rtm->rtm_seq != rtm_seq || rtm->rtm_pid != pid) {
231	    continue;
232	}
233	info.rti_addrs = rtm->rtm_addrs;
234	/* ALIGN: rtmsg.data is aligned to sizeof(struct rt_msghdr) */
235	if (rt_xaddrs((const char*)(void *)rtmsg.data,
236                  (const char*)(void *)rtmsg.data + n, &info) != 0) {
237	    syslog(LOG_DEBUG, "subnet_route_if_index: rt_xaddrs failed");
238	    break;
239	}
240	sdl = (struct sockaddr_dl *)(void *)info.rti_info[RTAX_GATEWAY];
241	if (sdl == NULL || sdl->sdl_family != AF_LINK) {
242	    syslog(LOG_DEBUG,
243		   "subnet_route_if_index: can't get interface name");
244	    break;
245	}
246	ret_if_index = sdl->sdl_index;
247	break;
248    }
249 done:
250    if (sockfd >= 0) {
251	close(sockfd);
252    }
253    return (ret_if_index);
254}
255
256
257/*
258 * Function: subnet_route
259 * Purpose:
260 *    Add/remove/get the specified subnet route.
261 * Returns:
262 *    TRUE if operation was successful, FALSE otherwise.
263 */
264STATIC boolean_t
265subnet_route(int cmd, struct in_addr gateway, struct in_addr netaddr,
266	     struct in_addr netmask, const char * ifname)
267{
268    int 			len;
269    boolean_t			ret = TRUE;
270    struct rt_msghdr *		rtm;
271    struct {
272	struct rt_msghdr	hdr;
273	struct sockaddr_in	dst;
274	struct sockaddr_in	gway;
275	struct sockaddr_in	mask;
276	struct sockaddr_dl	ifp;
277	struct sockaddr_in	ifa;
278    } 				rtmsg;
279    int 			sockfd = -1;
280
281    sockfd = arp_open_routing_socket();
282    if (sockfd < 0) {
283	my_log(LOG_NOTICE, "subnet_route: open routing socket failed, %s",
284	       strerror(errno));
285	ret = FALSE;
286	goto done;
287    }
288
289    memset(&rtmsg, 0, sizeof(rtmsg));
290    rtm = &rtmsg.hdr;
291    rtm->rtm_type = cmd;
292    rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_CLONING;
293    rtm->rtm_version = RTM_VERSION;
294    rtm->rtm_seq = arp_get_next_seq();
295    rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
296    set_sockaddr_in(&rtmsg.dst, netaddr);
297    set_sockaddr_in(&rtmsg.gway, gateway);
298    set_sockaddr_in(&rtmsg.mask, netmask);
299
300    len = sizeof(rtmsg);
301    if (ifname != NULL) {
302	rtm->rtm_addrs |= RTA_IFP | RTA_IFA;
303	/* copy the interface name */
304	rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
305	rtmsg.ifp.sdl_family = AF_LINK;
306	rtmsg.ifp.sdl_nlen = strlen(ifname);
307	bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
308	/* and the interface address (which is the gateway) */
309	set_sockaddr_in(&rtmsg.ifa, gateway);
310    }
311    else {
312	/* no ifp/ifa information */
313	len -= sizeof(rtmsg.ifp) + sizeof(rtmsg.ifa);
314    }
315    rtm->rtm_msglen = len;
316    if (write(sockfd, &rtmsg, len) < 0) {
317	int	error = errno;
318
319	switch (error) {
320	case ESRCH:
321	case EEXIST:
322	    my_log(LOG_DEBUG, "subnet_route: write routing socket failed, %s",
323		   strerror(error));
324	    break;
325	default:
326	    my_log(LOG_NOTICE, "subnet_route: write routing socket failed, %s",
327		   strerror(error));
328	    break;
329	}
330	ret = FALSE;
331    }
332 done:
333    if (sockfd >= 0) {
334	close(sockfd);
335    }
336    return (ret);
337}
338
339PRIVATE_EXTERN boolean_t
340subnet_route_add(struct in_addr gateway, struct in_addr netaddr,
341		 struct in_addr netmask, const char * ifname)
342{
343    return (subnet_route(RTM_ADD, gateway, netaddr, netmask, ifname));
344}
345
346PRIVATE_EXTERN boolean_t
347subnet_route_delete(struct in_addr gateway, struct in_addr netaddr,
348		    struct in_addr netmask)
349{
350    return (subnet_route(RTM_DELETE, gateway, netaddr, netmask, NULL));
351}
352
353
354#define N_MIB		6
355
356STATIC int
357flush_dynamic_routes(int s)
358{
359    char *		buf = NULL;
360    int			i;
361    char *		lim;
362    int 		mib[N_MIB];
363    size_t 		needed;
364    char *		next;
365    struct rt_msghdr *	rtm;
366    struct sockaddr_in *sin;
367
368    mib[0] = CTL_NET;
369    mib[1] = PF_ROUTE;
370    mib[2] = 0;
371    mib[3] = AF_INET;
372    mib[4] = NET_RT_FLAGS;
373    mib[5] = RTF_DYNAMIC;
374    for (i = 0; i < 3; i++) {
375	if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
376	    break;
377	}
378	if ((buf = malloc(needed)) == NULL) {
379	    break;
380	}
381	if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
382	    break;
383	}
384	free(buf);
385	buf = NULL;
386    }
387    if (buf == NULL) {
388	return (-1);
389    }
390    lim = buf + needed;
391    for (next = buf; next < lim; next += rtm->rtm_msglen) {
392	/* ALIGN: assume kernel provides necessary alignment */
393	rtm = (struct rt_msghdr *)(void *)next;
394	sin = (struct sockaddr_in *)(rtm + 1);
395
396	rtm->rtm_type = RTM_DELETE;
397	rtm->rtm_seq = arp_get_next_seq();
398	if (write(s, rtm, rtm->rtm_msglen) < 0) {
399	    my_log(LOG_NOTICE,
400		   "IPConfiguration: removing dynamic route for "
401		   IP_FORMAT " failed, %s",
402		   IP_LIST(&sin->sin_addr),
403		   strerror(errno));
404	}
405	else if (G_IPConfiguration_verbose) {
406	    my_log(LOG_DEBUG,
407		   "IPConfiguration: removed dynamic route for " IP_FORMAT,
408		   IP_LIST(&sin->sin_addr));
409	}
410    }
411    free(buf);
412    return (0);
413}
414
415PRIVATE_EXTERN void
416flush_routes(int if_index, const struct in_addr ip,
417	     const struct in_addr broadcast)
418{
419    int		s;
420
421    s = arp_open_routing_socket();
422    if (s < 0) {
423	return;
424    }
425
426    /* remove permanent arp entries for the IP and IP broadcast.
427     * - do these first because they require reading from the routing socket
428     * - flushing only requires writing to the routing socket
429     */
430    if (ip.s_addr) {
431	(void)arp_delete(s, ip, 0);
432    }
433    if (broadcast.s_addr) {
434	(void)arp_delete(s, broadcast, 0);
435    }
436
437    /* blow away all non-permanent arp entries */
438    (void)arp_flush(s, FALSE, if_index);
439
440    (void)flush_dynamic_routes(s);
441    close(s);
442    return;
443}
444