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