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
69/*
70 * Function: subnet_route
71 * Purpose:
72 *    Add/remove/get the specified subnet route.
73 * Returns:
74 *    TRUE if operation was successful, FALSE otherwise.
75 */
76STATIC boolean_t
77subnet_route(int cmd, struct in_addr gateway, struct in_addr netaddr,
78	     struct in_addr netmask, const char * ifname)
79{
80    int 			len;
81    boolean_t			ret = TRUE;
82    struct rt_msghdr *		rtm;
83    struct {
84	struct rt_msghdr	hdr;
85	struct sockaddr_in	dst;
86	struct sockaddr_in	gway;
87	struct sockaddr_in	mask;
88	struct sockaddr_dl	ifp;
89	struct sockaddr_in	ifa;
90    } 				rtmsg;
91    int 			sockfd = -1;
92
93    sockfd = arp_open_routing_socket();
94    if (sockfd < 0) {
95	my_log(LOG_NOTICE, "subnet_route: open routing socket failed, %s",
96	       strerror(errno));
97	ret = FALSE;
98	goto done;
99    }
100
101    memset(&rtmsg, 0, sizeof(rtmsg));
102    rtm = &rtmsg.hdr;
103    rtm->rtm_type = cmd;
104    rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_CLONING;
105    rtm->rtm_version = RTM_VERSION;
106    rtm->rtm_seq = arp_get_next_seq();
107    rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
108    set_sockaddr_in(&rtmsg.dst, netaddr);
109    set_sockaddr_in(&rtmsg.gway, gateway);
110    set_sockaddr_in(&rtmsg.mask, netmask);
111
112    len = sizeof(rtmsg);
113    if (ifname != NULL) {
114	rtm->rtm_addrs |= RTA_IFP | RTA_IFA;
115	/* copy the interface name */
116	rtmsg.ifp.sdl_len = sizeof(rtmsg.ifp);
117	rtmsg.ifp.sdl_family = AF_LINK;
118	rtmsg.ifp.sdl_nlen = strlen(ifname);
119	bcopy(ifname, rtmsg.ifp.sdl_data, rtmsg.ifp.sdl_nlen);
120	/* and the interface address (which is the gateway) */
121	set_sockaddr_in(&rtmsg.ifa, gateway);
122    }
123    else {
124	/* no ifp/ifa information */
125	len -= sizeof(rtmsg.ifp) + sizeof(rtmsg.ifa);
126    }
127    rtm->rtm_msglen = len;
128    if (write(sockfd, &rtmsg, len) < 0) {
129	int	error = errno;
130
131	switch (error) {
132	case ESRCH:
133	case EEXIST:
134	    my_log(LOG_DEBUG, "subnet_route: write routing socket failed, %s",
135		   strerror(error));
136	    break;
137	default:
138	    my_log(LOG_NOTICE, "subnet_route: write routing socket failed, %s",
139		   strerror(error));
140	    break;
141	}
142	ret = FALSE;
143    }
144 done:
145    if (sockfd >= 0) {
146	close(sockfd);
147    }
148    return (ret);
149}
150
151PRIVATE_EXTERN boolean_t
152subnet_route_add(struct in_addr gateway, struct in_addr netaddr,
153		 struct in_addr netmask, const char * ifname)
154{
155    return (subnet_route(RTM_ADD, gateway, netaddr, netmask, ifname));
156}
157
158#define N_MIB		6
159
160STATIC int
161flush_dynamic_routes(int s)
162{
163    char *		buf = NULL;
164    int			i;
165    char *		lim;
166    int 		mib[N_MIB];
167    size_t 		needed;
168    char *		next;
169    struct rt_msghdr *	rtm;
170    struct sockaddr_in *sin;
171
172    mib[0] = CTL_NET;
173    mib[1] = PF_ROUTE;
174    mib[2] = 0;
175    mib[3] = AF_INET;
176    mib[4] = NET_RT_FLAGS;
177    mib[5] = RTF_DYNAMIC;
178    for (i = 0; i < 3; i++) {
179	if (sysctl(mib, N_MIB, NULL, &needed, NULL, 0) < 0) {
180	    break;
181	}
182	if ((buf = malloc(needed)) == NULL) {
183	    break;
184	}
185	if (sysctl(mib, N_MIB, buf, &needed, NULL, 0) >= 0) {
186	    break;
187	}
188	free(buf);
189	buf = NULL;
190    }
191    if (buf == NULL) {
192	return (-1);
193    }
194    lim = buf + needed;
195    for (next = buf; next < lim; next += rtm->rtm_msglen) {
196	/* ALIGN: assume kernel provides necessary alignment */
197	rtm = (struct rt_msghdr *)(void *)next;
198	sin = (struct sockaddr_in *)(rtm + 1);
199
200	rtm->rtm_type = RTM_DELETE;
201	rtm->rtm_seq = arp_get_next_seq();
202	if (write(s, rtm, rtm->rtm_msglen) < 0) {
203	    my_log(LOG_NOTICE,
204		   "IPConfiguration: removing dynamic route for "
205		   IP_FORMAT " failed, %s",
206		   IP_LIST(&sin->sin_addr),
207		   strerror(errno));
208	}
209	else if (G_IPConfiguration_verbose) {
210	    my_log(LOG_DEBUG,
211		   "IPConfiguration: removed dynamic route for " IP_FORMAT,
212		   IP_LIST(&sin->sin_addr));
213	}
214    }
215    free(buf);
216    return (0);
217}
218
219PRIVATE_EXTERN void
220flush_routes(int if_index, const struct in_addr ip,
221	     const struct in_addr broadcast)
222{
223    int		s;
224
225    s = arp_open_routing_socket();
226    if (s < 0) {
227	return;
228    }
229
230    /* remove permanent arp entries for the IP and IP broadcast.
231     * - do these first because they require reading from the routing socket
232     * - flushing only requires writing to the routing socket
233     */
234    if (ip.s_addr) {
235	(void)arp_delete(s, ip, 0);
236    }
237    if (broadcast.s_addr) {
238	(void)arp_delete(s, broadcast, 0);
239    }
240
241    /* blow away all non-permanent arp entries */
242    (void)arp_flush(s, FALSE, if_index);
243
244    (void)flush_dynamic_routes(s);
245    close(s);
246    return;
247}
248