/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * sys-bsd.c - System-dependent procedures for setting up * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.) * * Copyright (c) 1989 Carnegie Mellon University. * Copyright (c) 1995 The Australian National University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Carnegie Mellon University and The Australian National University. * The names of the Universities may not be used to endorse or promote * products derived from this software without specific prior written * permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #define RCSID "$Id: sys-MacOSX.c,v 1.38 2005/03/11 05:48:32 lindak Exp $" /* ----------------------------------------------------------------------------- Includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PPP_FILTER #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pppcontroller.h" #include #include "../vpnd/RASSchemaDefinitions.h" #include "pppd.h" #include "fsm.h" #include "ipcp.h" #include "lcp.h" #include "eap.h" #include "../vpnd/RASSchemaDefinitions.h" /* ----------------------------------------------------------------------------- Definitions ----------------------------------------------------------------------------- */ #define IP_FORMAT "%d.%d.%d.%d" #define IP_CH(ip) ((u_char *)(ip)) #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] #define PPP_NKE_PATH "/System/Library/Extensions/PPP.kext" /* We can get an EIO error on an ioctl if the modem has hung up */ #define ok_error(num) ((num)==EIO) #ifndef N_SYNC_PPP #define N_SYNC_PPP 14 #endif /* * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name. */ #if !defined(PPP_DRV_NAME) #define PPP_DRV_NAME "ppp" #endif /* !defined(PPP_DRV_NAME) */ /* ----------------------------------------------------------------------------- Forward declarations ----------------------------------------------------------------------------- */ static int get_flags (int fd); static void set_flags (int fd, int flags); static int set_kdebugflag(int level); static int make_ppp_unit(void); /* Prototypes for procedures local to this file. */ static int get_ether_addr __P((u_int32_t, struct sockaddr_dl *)); static int connect_pfppp(); //static void sys_pidchange(void *arg, int pid); static void sys_phasechange(void *arg, int phase); static void sys_exitnotify(void *arg, int exitcode); int sys_getconsoleuser(uid_t *uid); int publish_keyentry(CFStringRef key, CFStringRef entry, CFTypeRef value); int publish_dictnumentry(CFStringRef dict, CFStringRef entry, int val); int publish_dictstrentry(CFStringRef dict, CFStringRef entry, char *str, int encoding); int unpublish_keyentry(CFStringRef key, CFStringRef entry); int unpublish_dict(CFStringRef dict); int publish_dns_entry(CFStringRef property1, CFTypeRef ref1, CFStringRef property2, CFTypeRef ref2, CFStringRef property3, CFTypeRef ref3, int clean); int unpublish_dictentry(CFStringRef dict, CFStringRef entry); static void sys_eventnotify(void *param, int code); static void sys_timeremaining(void *param, int info); static void sys_authpeersuccessnotify(void *param, int info); int publish_stateaddr(u_int32_t o, u_int32_t h, u_int32_t m); int route_interface(int cmd, struct in_addr host, struct in_addr mask, char iftype, char *ifname, int is_host); int route_gateway(int cmd, struct in_addr dest, struct in_addr mask, struct in_addr gateway, int use_gway_flag); /* ----------------------------------------------------------------------------- Globals ----------------------------------------------------------------------------- */ #ifndef lint static const char rcsid[] = RCSID; #endif static int ttydisc = TTYDISC; /* The default tty discipline */ static int pppdisc = PPPDISC; /* The PPP sync or async discipline */ static int initfdflags = -1; /* Initial file descriptor flags for ppp_fd */ static int ppp_fd = -1; /* fd which is set to PPP discipline */ static int rtm_seq; static int restore_term; /* 1 => we've munged the terminal */ static struct termios inittermios; /* Initial TTY termios */ static struct winsize wsinfo; /* Initial window size info */ static int ip_sockfd; /* socket for doing interface ioctls */ static fd_set in_fds; /* set of fds that wait_input waits for */ static fd_set ready_fds; /* set of fds currently ready (out of select) */ static int max_in_fd; /* highest fd set in in_fds */ static int if_is_up; /* the interface is currently up */ static int ipv4_plumbed = 0; /* is ipv4 plumbed on the interface ? */ static u_int32_t ifaddrs[2]; /* local and remote addresses we set */ static u_int32_t default_route_gateway; /* gateway addr for default route */ static u_int32_t proxy_arp_addr; /* remote addr for proxy arp */ SCDynamicStoreRef cfgCache = 0; /* configd session */ CFStringRef serviceidRef = 0; /* service id ref */ CFStringRef serveridRef = 0; /* server id ref */ extern u_char inpacket_buf[]; /* borrowed from main.c */ static u_int32_t connecttime; /* time when connection occured */ int looped; /* 1 if using loop */ int ppp_sockfd = -1; /* fd for PF_PPP socket */ char *serviceid = NULL; /* configuration service ID to publish */ char *serverid = NULL; /* server ID that spwaned this service */ bool noload = 0; /* don't load the kernel extension */ bool looplocal = 0; /* Don't loop local traffic destined to the local address some applications rely on this default behavior */ bool addifroute = 0; /* install route for the netmask of the interface */ static struct in_addr ifroute_address; static struct in_addr ifroute_mask; static int ifroute_installed = 0; double timeScaleSeconds; /* scale factor for machine absolute time to seconds */ double timeScaleMicroSeconds; /* scale factor for machine absolute time to microseconds */ CFPropertyListRef userOptions = NULL; CFPropertyListRef systemOptions = NULL; option_t sys_options[] = { { "serviceid", o_string, &serviceid, "Service ID to publish"}, { "serverid", o_string, &serverid, "Server ID that spawned this service."}, { "nopppload", o_bool, &noload, "Don't try to load PPP NKE", 1}, { "looplocal", o_bool, &looplocal, "Loop local traffic destined to the local address", 1}, { "noifroute", o_bool, &addifroute, "Don't install route for the interface", 0}, { "addifroute", o_bool, &addifroute, "Install route for the interface", 1}, { "nolooplocal", o_bool, &looplocal, "Don't loop local traffic destined to the local address", 0}, { NULL } }; /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void closeall() { int i; for (i = getdtablesize() - 1; i >= 0; i--) close(i); open("/dev/null", O_RDWR, 0); dup(0); dup(0); return; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ u_long load_kext(char *kext) { int pid; if ((pid = fork()) < 0) return 1; if (pid == 0) { closeall(); // PPP kernel extension not loaded, try load it... execl("/sbin/kextload", "kextload", kext, (char *)0); exit(1); } while (waitpid(pid, 0, 0) < 0) { if (errno == EINTR) continue; return 1; } return 0; } /* ----------------------------------------------------------------------------- preinitialize options, called before sysinit ----------------------------------------------------------------------------- */ void sys_install_options() { add_options(sys_options); } /* ------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- */ CFPropertyListRef Unserialize(void *data, u_int32_t dataLen) { CFDataRef xml; CFStringRef xmlError; CFPropertyListRef ref = 0; xml = CFDataCreate(NULL, data, dataLen); if (xml) { ref = CFPropertyListCreateFromXMLData(NULL, xml, kCFPropertyListImmutable, &xmlError); CFRelease(xml); } return ref; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void CopyControllerData() { mach_port_t server; kern_return_t status; void *data = NULL; int datalen; int result = kSCStatusFailed; status = bootstrap_look_up(bootstrap_port, PPPCONTROLLER_SERVER, &server); switch (status) { case BOOTSTRAP_SUCCESS : /* service currently registered, "a good thing" (tm) */ break; case BOOTSTRAP_UNKNOWN_SERVICE : /* service not currently registered, try again later */ return ; default : return; } status = pppcontroller_copyprivoptions(server, mach_task_self(), 0, (xmlDataOut_t *)&data, &datalen, &result); if (status != KERN_SUCCESS || result != kSCStatusOK) { error("cannot get private system options from controller\n"); return; } systemOptions = Unserialize(data, datalen); status = pppcontroller_copyprivoptions(server, mach_task_self(), 1, (xmlDataOut_t *)&data, &datalen, &result); if (status != KERN_SUCCESS || result != kSCStatusOK) { error("cannot get private user options from controller\n"); return; } userOptions = Unserialize(data, datalen); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void CopyServerData() { SCPreferencesRef prefs = 0; CFPropertyListRef servers_list; // open the prefs file prefs = SCPreferencesCreate(0, CFSTR("pppd"), kRASServerPrefsFileName); if (prefs == NULL) { fatal("Cannot open servers plist\n"); return; } // get servers list from the plist servers_list = SCPreferencesGetValue(prefs, kRASServers); if (servers_list == NULL) { fatal("No servers found in servers plist\n"); CFRelease(prefs); return; } systemOptions = CFDictionaryGetValue(servers_list, serveridRef); if (!systemOptions || CFGetTypeID(systemOptions) != CFDictionaryGetTypeID()) { fatal("Server ID '%s' not found in servers plist\n", serverid); systemOptions = 0; CFRelease(prefs); return; } CFRetain(systemOptions); CFRelease(prefs); } /* ----------------------------------------------------------------------------- System-dependent initialization ----------------------------------------------------------------------------- */ void sys_init() { int flags; mach_timebase_info_data_t timebaseInfo; openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP); setlogmask(LOG_UPTO(LOG_INFO)); if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); // establish pppd as session leader // if started via terminal, setsid will fail, which doesn't matter // if started via configd, setsid will succeed and will allow reception of SIGHUP setsid(); // open a socket to the PF_PPP protocol ppp_sockfd = connect_pfppp(); if (ppp_sockfd < 0) fatal("Couldn't open PF_PPP: %m"); flags = fcntl(ppp_sockfd, F_GETFL); if (flags == -1 || fcntl(ppp_sockfd, F_SETFL, flags | O_NONBLOCK) == -1) warning("Couldn't set PF_PPP to nonblock: %m"); // Get an internet socket for doing socket ioctls ip_sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (ip_sockfd < 0) fatal("Couldn't create IP socket: %m(%d)", errno); // serviceid is required if we want pppd to publish information into the cache if (!serviceid) { CFUUIDRef uuid; CFStringRef strref; char str[100]; uuid = CFUUIDCreate(NULL); strref = CFUUIDCreateString(NULL, uuid); CFStringGetCString(strref, str, sizeof(str), kCFStringEncodingUTF8); if (serviceid = malloc(strlen(str) + 1)) strcpy(serviceid, str); else fatal("Couldn't allocate memory to create temporary service id: %m(%d)", errno); CFRelease(strref); CFRelease(uuid); } serviceidRef = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), serviceid); if (!serviceidRef) fatal("Couldn't allocate memory to create service id reference: %m(%d)", errno); /* if started as a client by PPPController copy user and system options from the controller */ if (controlled) { CopyControllerData(); } //sys_pidchange(0, getpid()); cfgCache = SCDynamicStoreCreate(0, CFSTR("pppd"), 0, 0); if (cfgCache == 0) fatal("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); // if we are going to detach, wait to publish pid if (nodetach) publish_dictnumentry(kSCEntNetPPP, CFSTR("pid"), getpid()); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPStatus, phase); if (serverid) { serveridRef = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), serverid); if (!serveridRef) fatal("Couldn't allocate memory to create server id reference: %m(%d)", errno); /* copy system options from the server plist */ CopyServerData(); publish_dictstrentry(kSCEntNetInterface, CFSTR("ServerID"), serverid, kCFStringEncodingMacRoman); } //add_notifier(&pidchange, sys_pidchange, 0); add_notifier(&phasechange, sys_phasechange, 0); add_notifier(&exitnotify, sys_exitnotify, 0); // only send event if started when we are started by the PPPController if (statusfd != -1) { add_notifier(&ip_up_notify, sys_eventnotify, (void*)PPP_EVT_IPCP_UP); add_notifier(&ip_down_notify, sys_eventnotify, (void*)PPP_EVT_IPCP_DOWN); add_notifier(&lcp_up_notify, sys_eventnotify, (void*)PPP_EVT_LCP_UP); add_notifier(&lcp_down_notify, sys_eventnotify, (void*)PPP_EVT_LCP_DOWN); add_notifier(&lcp_lowerup_notify, sys_eventnotify, (void*)PPP_EVT_LOWERLAYER_UP); add_notifier(&lcp_lowerdown_notify, sys_eventnotify, (void*)PPP_EVT_LOWERLAYER_DOWN); add_notifier(&auth_start_notify, sys_eventnotify, (void*)PPP_EVT_AUTH_STARTED); add_notifier(&auth_withpeer_fail_notify, sys_eventnotify, (void*)PPP_EVT_AUTH_FAILED); add_notifier(&auth_withpeer_success_notify, sys_eventnotify, (void*)PPP_EVT_AUTH_SUCCEDED); add_notifier(&connectscript_started_notify, sys_eventnotify, (void*)PPP_EVT_CONNSCRIPT_STARTED); add_notifier(&connectscript_finished_notify, sys_eventnotify, (void*)PPP_EVT_CONNSCRIPT_FINISHED); add_notifier(&terminalscript_started_notify, sys_eventnotify, (void*)PPP_EVT_TERMSCRIPT_STARTED); add_notifier(&terminalscript_finished_notify, sys_eventnotify, (void*)PPP_EVT_TERMSCRIPT_FINISHED); add_notifier(&connect_started_notify, sys_eventnotify, (void*)PPP_EVT_CONN_STARTED); add_notifier(&connect_success_notify, sys_eventnotify, (void*)PPP_EVT_CONN_SUCCEDED); add_notifier(&connect_fail_notify, sys_eventnotify, (void*)PPP_EVT_CONN_FAILED); add_notifier(&disconnect_started_notify, sys_eventnotify, (void*)PPP_EVT_DISC_STARTED); add_notifier(&disconnect_done_notify, sys_eventnotify, (void*)PPP_EVT_DISC_FINISHED); add_notifier(&stop_notify, sys_eventnotify, (void*)PPP_EVT_STOPPED); add_notifier(&cont_notify, sys_eventnotify, (void*)PPP_EVT_CONTINUED); } add_notifier(&lcp_timeremaining_notify, sys_timeremaining, 0); add_notifier(&auth_peer_success_notify, sys_authpeersuccessnotify, 0); if (mach_timebase_info(&timebaseInfo) == KERN_SUCCESS) { // returns scale factor for ns timeScaleMicroSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000; timeScaleSeconds = timeScaleMicroSeconds / 1000000; } FD_ZERO(&in_fds); FD_ZERO(&ready_fds); max_in_fd = 0; } /* ----------------------------------------------------------------------------- sys_cleanup - restore any system state we modified before exiting: mark the interface down, delete default route and/or proxy arp entry. This should call die() because it's called from die() ----------------------------------------------------------------------------- */ void sys_cleanup() { struct ifreq ifr; cifroute(); if (if_is_up) { strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCGIFFLAGS, &ifr) >= 0 && ((ifr.ifr_flags & IFF_UP) != 0)) { ifr.ifr_flags &= ~IFF_UP; ioctl(ip_sockfd, SIOCSIFFLAGS, &ifr); } } if (ifaddrs[0] != 0) cifaddr(0, ifaddrs[0], ifaddrs[1]); if (default_route_gateway) cifdefaultroute(0, 0, default_route_gateway); if (proxy_arp_addr) cifproxyarp(0, proxy_arp_addr); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void sys_close() { if (ip_sockfd != -1) { close(ip_sockfd); ip_sockfd = -1; } if (ppp_sockfd != -1) { close(ppp_sockfd); ppp_sockfd = -1; } } /* ----------------------------------------------------------------------------- Functions to read and set the flags value in the device driver ----------------------------------------------------------------------------- */ static int get_flags (int fd) { int flags; if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) { if ( ok_error (errno) ) flags = 0; else fatal("ioctl(PPPIOCGFLAGS): %m"); } SYSDEBUG ((LOG_DEBUG, "get flags = %x\n", flags)); return flags; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static void set_flags (int fd, int flags) { SYSDEBUG ((LOG_DEBUG, "set flags = %x\n", flags)); if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0) { if (! ok_error (errno) ) fatal("ioctl(PPPIOCSFLAGS, %x): %m", flags, errno); } } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void sys_notify(u_int32_t message, u_int32_t code1, u_int32_t code2) { struct ppp_msg_hdr *hdr; int servlen, totlen; u_char *p, *msg; if (statusfd == -1) return; servlen = strlen(serviceid); totlen = sizeof(struct ppp_msg_hdr) + servlen + 8; msg = malloc(totlen); if (!msg) { warning("no memory to send event to PPPController"); return; } p = msg; bzero(p, totlen); hdr = (struct ppp_msg_hdr *)p; hdr->m_type = message; hdr->m_len = 8; hdr->m_flags |= USE_SERVICEID; hdr->m_link = servlen; p += sizeof(struct ppp_msg_hdr); bcopy(serviceid, p, servlen); p += servlen; bcopy((u_char*)&code1, p, 4); p += 4; bcopy((u_char*)&code2, p, 4); if (write(statusfd, msg, totlen) != totlen) { warning("can't talk to PPPController : %m"); } free(msg); } /* ----------------------------------------------------------------------------- we installed the notifier with the event as the parameter ----------------------------------------------------------------------------- */ void sys_eventnotify(void *param, int code) { if (param == (void*)PPP_EVT_CONN_FAILED) code = EXIT_CONNECT_FAILED; sys_notify(PPPD_EVENT, (u_int32_t)param, code); } /* ----------------------------------------------------------------------------- send status notification to the controller ----------------------------------------------------------------------------- */ void sys_statusnotify() { sys_notify(PPPD_STATUS, status, devstatus); } /* ----------------------------------------------------------------------------- check the options that the user specified ----------------------------------------------------------------------------- */ int sys_check_options() { #ifndef CDTRCTS if (crtscts == 2) { warning("DTR/CTS flow control is not supported on this system"); return 0; } #endif return 1; } /* ----------------------------------------------------------------------------- check if the kernel supports PPP ----------------------------------------------------------------------------- */ int ppp_available() { int s; extern char *no_ppp_msg; no_ppp_msg = "\ Mac OS X lacks kernel support for PPP. \n\ To include PPP support in the kernel, please follow \n\ the steps detailed in the README.MacOSX file.\n"; // open to socket to the PF_PPP family // if that works, the kernel extension is loaded. if ((s = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL)) < 0) { if (!noload && !load_kext(PPP_NKE_PATH)) s = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL); if (s < 0) return 0; } // could be smarter and get the version of the ppp family, // using get option or ioctl close(s); return 1; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ static int still_ppp(void) { return !hungup && ppp_fd >= 0; } /* ----------------------------------------------------------------------------- Define the debugging level for the kernel ----------------------------------------------------------------------------- */ static int set_kdebugflag (int requested_level) { if (ifunit < 0) return 1; if (ioctl(ppp_sockfd, PPPIOCSDEBUG, &requested_level) < 0) { if ( ! ok_error (errno) ) error("ioctl(PPPIOCSDEBUG): %m"); return (0); } SYSDEBUG ((LOG_INFO, "set kernel debugging level to %d", requested_level)); return (1); } /* ----------------------------------------------------------------------------- make a new ppp unit for ppp_sockfd ----------------------------------------------------------------------------- */ static int make_ppp_unit() { int x; char name[32]; ifunit = req_unit; x = ioctl(ppp_sockfd, PPPIOCNEWUNIT, &ifunit); if (x < 0 && req_unit >= 0 && errno == EEXIST) { warning("Couldn't allocate PPP unit %d as it is already in use"); ifunit = -1; x = ioctl(ppp_sockfd, PPPIOCNEWUNIT, &ifunit); } if (x < 0) error("Couldn't create new ppp unit: %m"); else { slprintf(name, sizeof(name), "%s%d", PPP_DRV_NAME, ifunit); publish_dictstrentry(kSCEntNetPPP, kSCPropInterfaceName, name, kCFStringEncodingMacRoman); } return x; } /* ----------------------------------------------------------------------------- coen a socket to the PF_PPP protocol ----------------------------------------------------------------------------- */ int connect_pfppp() { int fd = -1; struct sockaddr_ppp pppaddr; // open a PF_PPP socket fd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL); if (fd < 0) { error("Couldn't open PF_PPP: %m"); return -1; } // need to connect to the PPP protocol pppaddr.ppp_len = sizeof(struct sockaddr_ppp); pppaddr.ppp_family = AF_PPP; pppaddr.ppp_proto = PPPPROTO_CTL; pppaddr.ppp_cookie = 0; if (connect(fd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0) { error("Couldn't connect to PF_PPP: %m"); close(fd); return -1; } return fd; } /* ----------------------------------------------------------------------------- Turn the serial port into a ppp interface ----------------------------------------------------------------------------- */ int tty_establish_ppp (int tty_fd) { int new_fd; // First, turn the device into ppp link /* Ensure that the tty device is in exclusive mode. */ if (ioctl(tty_fd, TIOCEXCL, 0) < 0) { if ( ! ok_error ( errno )) ;//warning("Couldn't make tty exclusive: %m"); } // Set the current tty to the PPP discpline pppdisc = sync_serial ? N_SYNC_PPP: PPPDISC; if (ioctl(tty_fd, TIOCSETD, &pppdisc) < 0) { if ( ! ok_error (errno) ) { error("Couldn't set tty to PPP discipline: %m"); return -1; } } // Then, then do the generic link work, and get a generic fd back new_fd = generic_establish_ppp(tty_fd); if (new_fd == -1) { // Restore the previous line discipline if (ioctl(tty_fd, TIOCSETD, &ttydisc) < 0) if ( ! ok_error (errno)) error("ioctl(TIOCSETD, TTYDISC): %m"); return -1; } set_flags(new_fd, get_flags(ppp_fd) & ~(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)); return new_fd; } /* ----------------------------------------------------------------------------- Restore the serial port to normal operation. This shouldn't call die() because it's called from die() ----------------------------------------------------------------------------- */ void tty_disestablish_ppp(int tty_fd) { if (!hungup) { // Flush the tty output buffer so that the TIOCSETD doesn't hang. //if (tcflush(tty_fd, TCIOFLUSH) < 0) // warning("tcflush failed: %m"); // Restore the previous line discipline if (ioctl(tty_fd, TIOCSETD, &ttydisc) < 0) { if ( ! ok_error (errno)) error("ioctl(TIOCSETD, TTYDISC): %m"); } if (ioctl(tty_fd, TIOCNXCL, 0) < 0) { if ( ! ok_error (errno)) warning("ioctl(TIOCNXCL): %m(%d)", errno); } // Reset non-blocking mode on fd if (initfdflags != -1 && fcntl(tty_fd, F_SETFL, initfdflags) < 0) { if ( ! ok_error (errno)) warning("Couldn't restore device fd flags: %m"); } } initfdflags = -1; generic_disestablish_ppp(tty_fd); } /* ----------------------------------------------------------------------------- generic code to establish ppp interface ----------------------------------------------------------------------------- */ int generic_establish_ppp (int fd) { int flags, s = -1, link = 0; // Open another instance of the ppp socket and connect the link to it if (ioctl(fd, PPPIOCGCHAN, &link) == -1) { error("Couldn't get link number: %m"); goto err; } dbglog("using link %d", link); // open a socket to the PPP protocol s = connect_pfppp(); if (s < 0) { error("Couldn't reopen PF_PPP: %m"); goto err; } if (ioctl(s, PPPIOCATTCHAN, &link) < 0) { error("Couldn't attach to the ppp link %d: %m", link); goto err_close; } flags = fcntl(s, F_GETFL); if (flags == -1 || fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) warning("Couldn't set ppp socket link to nonblock: %m"); /* set the ppp_fd socket now */ ppp_fd = s; if (!looped) ifunit = -1; if (!looped && !multilink) { // Create a new PPP unit. if (make_ppp_unit() < 0) goto err_close; } if (looped) { set_flags(ppp_sockfd, get_flags(ppp_sockfd) & ~SC_LOOP_TRAFFIC); looped = 0; } if (!multilink) { add_fd(ppp_sockfd); if (ioctl(s, PPPIOCCONNECT, &ifunit) < 0) { error("Couldn't attach to PPP unit %d: %m", ifunit); goto err_close; } } // Enable debug in the driver if requested. set_kdebugflag (kdebugflag); return ppp_fd; err_close: close(s); err: return -1; } /* ----------------------------------------------------------------------------- Restore the serial port to normal operation. This shouldn't call die() because it's called from die() fd is the file descriptor of the device ----------------------------------------------------------------------------- */ void generic_disestablish_ppp(int fd) { int x; close(ppp_fd); ppp_fd = -1; if (demand) { looped = 1; set_flags(ppp_sockfd, get_flags(ppp_sockfd) | SC_LOOP_TRAFFIC); } else { unpublish_dictentry(kSCEntNetPPP, kSCPropInterfaceName); if (ifunit >= 0 && ioctl(ppp_sockfd, PPPIOCDETACH, &x) < 0) error("Couldn't release PPP unit ppp_sockfd %d: %m", ppp_sockfd); } if (!multilink) remove_fd(ppp_sockfd); } /* ----------------------------------------------------------------------------- Check whether the link seems not to be 8-bit clean ----------------------------------------------------------------------------- */ void clean_check() { int x; char *s; if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) { s = NULL; switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) { case SC_RCV_B7_0: s = "bit 7 set to 1"; break; case SC_RCV_B7_1: s = "bit 7 set to 0"; break; case SC_RCV_EVNP: s = "odd parity"; break; case SC_RCV_ODDP: s = "even parity"; break; } if (s != NULL) { warning("Serial link is not 8-bit clean:"); warning("All received characters had %s", s); } } } /* ----------------------------------------------------------------------------- Set up the serial port on `fd' for 8 bits, no parity, * at the requested speed, etc. If `local' is true, set CLOCAL * regardless of whether the modem option was specified. * * For *BSD, we assume that speed_t values numerically equal bits/second ----------------------------------------------------------------------------- */ void set_up_tty(int fd, int local) { struct termios tios; // set the file descriptor as the controlling terminal, in order to receive SIGHUP if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) error("set_up_tty, can't set controlling terminal: %m"); if (tcgetattr(fd, &tios) < 0) { error("tcgetattr: %m"); return; } if (!restore_term) { inittermios = tios; ioctl(fd, TIOCGWINSZ, &wsinfo); } tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL); if (crtscts > 0 && !local) { if (crtscts == 2) { #ifdef CDTRCTS tios.c_cflag |= CDTRCTS; #endif } else tios.c_cflag |= CRTSCTS; } else if (crtscts < 0) { tios.c_cflag &= ~CRTSCTS; #ifdef CDTRCTS tios.c_cflag &= ~CDTRCTS; #endif } tios.c_cflag |= CS8 | CREAD | HUPCL; if (local || !modem) tios.c_cflag |= CLOCAL; tios.c_iflag = IGNBRK | IGNPAR; tios.c_oflag = 0; tios.c_lflag = 0; tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; if (crtscts == -2) { tios.c_iflag |= IXON | IXOFF; tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */ } if (inspeed) { cfsetospeed(&tios, inspeed); cfsetispeed(&tios, inspeed); } else { inspeed = cfgetospeed(&tios); /* * We can't proceed if the serial port speed is 0, * since that implies that the serial port is disabled. */ if (inspeed == 0) fatal("Baud rate for %s is 0; need explicit baud rate", devnam); } baud_rate = inspeed; if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) fatal("tcsetattr: %m"); restore_term = 1; } /* ----------------------------------------------------------------------------- Set up the serial port * If `local' is true, set CLOCAL * regardless of whether the modem option was specified. * other parameter (speed, start/stop bits, parity) may have been changed by the CCL ----------------------------------------------------------------------------- */ void set_up_tty_local(int fd, int local) { struct termios tios; if (tcgetattr(fd, &tios) < 0) { error("tcgetattr: %m"); return; } tios.c_cc[VMIN] = 1; tios.c_cc[VTIME] = 0; tios.c_cflag &= ~CLOCAL; if (local || !modem) tios.c_cflag |= CLOCAL; if (tcsetattr(fd, TCSANOW, &tios) < 0) fatal("tcsetattr: %m"); } /* ----------------------------------------------------------------------------- restore the terminal to the saved settings ----------------------------------------------------------------------------- */ void restore_tty(int fd) { if (restore_term) { if (!default_device) { /* * Turn off echoing, because otherwise we can get into * a loop with the tty and the modem echoing to each other. * We presume we are the sole user of this tty device, so * when we close it, it will revert to its defaults anyway. */ inittermios.c_lflag &= ~(ECHO | ECHONL); } if (!hungup) { if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0) if (errno != ENXIO) warning("tcsetattr: %m"); ioctl(fd, TIOCSWINSZ, &wsinfo); } restore_term = 0; } } /* ----------------------------------------------------------------------------- control the DTR line on the serial port. * This is called from die(), so it shouldn't call die() ----------------------------------------------------------------------------- */ void setdtr(int fd, int on) { int modembits = TIOCM_DTR; ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits); } /* ----------------------------------------------------------------------------- get a pty master/slave pair and chown the slave side * to the uid given. Assumes slave_name points to >= 12 bytes of space ----------------------------------------------------------------------------- */ int get_pty(int *master_fdp, int *slave_fdp, char *slave_name, int uid) { struct termios tios; if (openpty(master_fdp, slave_fdp, slave_name, NULL, NULL) < 0) return 0; fchown(*slave_fdp, uid, -1); fchmod(*slave_fdp, S_IRUSR | S_IWUSR); if (tcgetattr(*slave_fdp, &tios) == 0) { tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB); tios.c_cflag |= CS8 | CREAD; tios.c_iflag = IGNPAR | CLOCAL; tios.c_oflag = 0; tios.c_lflag = 0; if (tcsetattr(*slave_fdp, TCSAFLUSH, &tios) < 0) warning("couldn't set attributes on pty: %m"); } else warning("couldn't get attributes on pty: %m"); return 1; } /* ----------------------------------------------------------------------------- create the ppp interface and configure it in loopback mode. ----------------------------------------------------------------------------- */ int open_ppp_loopback() { looped = 1; /* allocate ourselves a ppp unit */ if (make_ppp_unit() < 0) die(1); set_flags(ppp_sockfd, SC_LOOP_TRAFFIC); set_kdebugflag(kdebugflag); ppp_fd = -1; return ppp_sockfd; } /* ----------------------------------------------------------------------------- Output PPP packet ----------------------------------------------------------------------------- */ void output(int unit, u_char *p, int len) { dump_packet("sent", p, len); // don't write FF03 len -= 2; p += 2; // link protocol are sent to the link // other protocols are send to the bundle if (write((*(u_short*)p >= 0xC000) ? ppp_fd : ppp_sockfd, p, len) < 0) { if (errno != EIO) error("write: %m"); } } /* ----------------------------------------------------------------------------- wait until there is data available, for the length of time specified by *timo (indefinite if timo is NULL) ----------------------------------------------------------------------------- */ void wait_input(struct timeval *timo) { int n; ready_fds = in_fds; n = select(max_in_fd + 1, &ready_fds, NULL, NULL, timo); if (n < 0 && errno != EINTR) fatal("select: %m"); } /* ----------------------------------------------------------------------------- wait on fd until there is data available, for the delay in milliseconds return 0 if timeout expires, < 0 if error, otherwise > 0 ----------------------------------------------------------------------------- */ int wait_input_fd(int fd, int delay) { fd_set ready; int n; struct timeval t; t.tv_sec = delay / 1000; t.tv_usec = delay % 1000; FD_ZERO(&ready); FD_SET(fd, &ready); do { n = select(fd + 1, &ready, NULL, &ready, &t); } while (n < 0 && errno == EINTR); if (n > 0) if (ioctl(fd, FIONREAD, &n) == -1) n = -1; return n; } /* ----------------------------------------------------------------------------- add an fd to the set that wait_input waits for ----------------------------------------------------------------------------- */ void add_fd(int fd) { FD_SET(fd, &in_fds); if (fd > max_in_fd) max_in_fd = fd; } /* ----------------------------------------------------------------------------- remove an fd from the set that wait_input waits for ----------------------------------------------------------------------------- */ void remove_fd(int fd) { FD_CLR(fd, &in_fds); } /* ----------------------------------------------------------------------------- return 1 is fd is set (i.e. select returned with this file descriptor set) ----------------------------------------------------------------------------- */ bool is_ready_fd(int fd) { return (FD_ISSET(fd, &ready_fds) != 0); } /* ----------------------------------------------------------------------------- get a PPP packet from the serial device ----------------------------------------------------------------------------- */ int read_packet(u_char *buf) { int len = -1; // FF03 are not read *buf++ = PPP_ALLSTATIONS; *buf++ = PPP_UI; // read first the socket attached to the link if (ppp_fd >= 0) { if ((len = read(ppp_fd, buf, PPP_MRU + PPP_HDRLEN - 2)) < 0) { if (errno != EWOULDBLOCK && errno != EINTR) error("read from socket link: %m"); } } // then, if nothing, link the socket attached to the bundle if (len < 0 && ifunit >= 0) { if ((len = read(ppp_sockfd, buf, PPP_MRU + PPP_HDRLEN - 2)) < 0) { if (errno != EWOULDBLOCK && errno != EINTR) error("read from socket bundle: %m"); } } return (len <= 0 ? len : len + 2); } /* ----------------------------------------------------------------------------- read characters from the loopback, form them * into frames, and detect when we want to bring the real link up. * Return value is 1 if we need to bring up the link, 0 otherwise ----------------------------------------------------------------------------- */ int get_loop_output() { int rv = 0; int n; while ((n = read_packet(inpacket_buf)) > 0) if (loop_frame(inpacket_buf, n)) rv = 1; return rv; } /* ----------------------------------------------------------------------------- configure the transmit characteristics of the ppp interface ----------------------------------------------------------------------------- */ void tty_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp) { u_int x; if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) fatal("ioctl(PPPIOCSASYNCMAP): %m"); if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) fatal("ioctl (PPPIOCGFLAGS): %m"); x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT; x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC; x = sync_serial ? x | SC_SYNC : x & ~SC_SYNC; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) fatal("ioctl(PPPIOCSFLAGS): %m"); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, pcomp); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, accomp); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, asyncmap); } /* ----------------------------------------------------------------------------- configure the transmit characteristics of the ppp interface function used for synchronous links, asyncmap is irrelevant ----------------------------------------------------------------------------- */ void generic_send_config(int mtu, u_int32_t asyncmap, int pcomp, int accomp) { u_int x; if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) fatal("ioctl (PPPIOCGFLAGS): %m"); x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT; x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) fatal("ioctl(PPPIOCSFLAGS): %m"); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, pcomp); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, accomp); } /* ----------------------------------------------------------------------------- set the MTU on the PPP network interface ----------------------------------------------------------------------------- */ void netif_set_mtu(int unit, int mtu) { struct ifreq ifr; strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); ifr.ifr_mtu = mtu; if (ioctl(ip_sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) error("ioctl (SIOCSIFMTU): %m"); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPMTU, mtu); } /* ----------------------------------------------------------------------------- get the MTU on the PPP network interface ----------------------------------------------------------------------------- */ int netif_get_mtu(int unit) { struct ifreq ifr; strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCGIFMTU, (caddr_t) &ifr) < 0) { error("ioctl (SIOCGIFMTU): %m"); return 0; } return ifr.ifr_mtu; } /* ----------------------------------------------------------------------------- stop traffic on this link ----------------------------------------------------------------------------- */ void ppp_hold(int unit) { u_int x; if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { warning("ioctl (PPPIOCGFLAGS): %m"); return; } x |= SC_HOLD; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) warning("ioctl(PPPIOCSFLAGS): %m"); } /* ----------------------------------------------------------------------------- resume traffic on this link ----------------------------------------------------------------------------- */ void ppp_cont(int unit) { u_int x; if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { warning("ioctl (PPPIOCGFLAGS): %m"); return; } x &= ~SC_HOLD; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) warning("ioctl(PPPIOCSFLAGS): %m"); } /* ----------------------------------------------------------------------------- set the extended transmit ACCM for the interface ----------------------------------------------------------------------------- */ void tty_set_xaccm(ext_accm accm) { if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY) warning("ioctl(set extended ACCM): %m"); } /* ----------------------------------------------------------------------------- configure the receive-side characteristics of the ppp interface. ----------------------------------------------------------------------------- */ void tty_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp) { int x; if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) fatal("ioctl(PPPIOCSMRU): %m"); if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) fatal("ioctl(PPPIOCSRASYNCMAP): %m"); if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) fatal("ioctl (PPPIOCGFLAGS): %m"); x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) fatal("ioctl(PPPIOCSFLAGS): %m"); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPMRU, mru); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, asyncmap); } /* ----------------------------------------------------------------------------- configure the receive-side characteristics of the ppp interface. function used for synchronous links, asyncmap is irrelevant ----------------------------------------------------------------------------- */ void generic_recv_config(int mru, u_int32_t asyncmap, int pcomp, int accomp) { int x; if (!still_ppp()) return; if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) fatal("ioctl(PPPIOCSMRU): %m"); if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) fatal("ioctl (PPPIOCGFLAGS): %m"); x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC; if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) fatal("ioctl(PPPIOCSFLAGS): %m"); publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPLCPMRU, mru); } /* ----------------------------------------------------------------------------- ask kernel whether a given compression method * is acceptable for use. Returns 1 if the method and parameters * are OK, 0 if the method is known but the parameters are not OK * (e.g. code size should be reduced), or -1 if the method is unknown ----------------------------------------------------------------------------- */ int ccp_test(int unit, u_char * opt_ptr, int opt_len, int for_transmit) { struct ppp_option_data data; data.ptr = opt_ptr; data.length = opt_len; data.transmit = for_transmit; if (ioctl(ppp_sockfd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0) return 1; return (errno == ENOBUFS)? 0: -1; } /* ----------------------------------------------------------------------------- inform kernel about the current state of CCP ----------------------------------------------------------------------------- */ void ccp_flags_set(int unit, int isopen, int isup) { int x; if (!still_ppp()) return; if (ioctl(ppp_sockfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { error("ioctl (PPPIOCGFLAGS): %m"); return; } x = isopen? x | SC_CCP_OPEN: x &~ SC_CCP_OPEN; x = isup? x | SC_CCP_UP: x &~ SC_CCP_UP; if (ioctl(ppp_sockfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) error("ioctl(PPPIOCSFLAGS): %m"); } /* ----------------------------------------------------------------------------- returns 1 if decompression was disabled as a * result of an error detected after decompression of a packet, * 0 otherwise. This is necessary because of patent nonsense ----------------------------------------------------------------------------- */ int ccp_fatal_error(int unit) { int x; if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { error("ioctl(PPPIOCGFLAGS): %m"); return 0; } return x & SC_DC_FERROR; } /* ----------------------------------------------------------------------------- return how long the link has been idle ----------------------------------------------------------------------------- */ int get_idle_time(int u, struct ppp_idle *ip) { return ioctl(ppp_sockfd, PPPIOCGIDLE, ip) >= 0; } /* ----------------------------------------------------------------------------- return statistics for the link ----------------------------------------------------------------------------- */ int get_ppp_stats(int u, struct pppd_stats *stats) { struct ifpppstatsreq req; memset (&req, 0, sizeof (req)); strlcpy(req.ifr_name, ifname, sizeof(req.ifr_name)); if (ioctl(ip_sockfd, SIOCGPPPSTATS, &req) < 0) { error("Couldn't get PPP statistics: %m"); return 0; } stats->bytes_in = req.stats.p.ppp_ibytes; stats->bytes_out = req.stats.p.ppp_obytes; stats->pkts_in = req.stats.p.ppp_ipackets; stats->pkts_out = req.stats.p.ppp_opackets; return 1; } #ifdef PPP_FILTER /* ----------------------------------------------------------------------------- transfer the pass and active filters to the kernel ----------------------------------------------------------------------------- */ int set_filters(struct bpf_program *pass, struct bpf_program *active) { int ret = 1; if (pass->bf_len > 0) { if (ioctl(ppp_fd, PPPIOCSPASS, pass) < 0) { error("Couldn't set pass-filter in kernel: %m"); ret = 0; } } if (active->bf_len > 0) { if (ioctl(ppp_fd, PPPIOCSACTIVE, active) < 0) { error("Couldn't set active-filter in kernel: %m"); ret = 0; } } return ret; } #endif /* ----------------------------------------------------------------------------- config tcp header compression ----------------------------------------------------------------------------- */ int sifvjcomp(int u, int vjcomp, int cidcomp, int maxcid) { u_int x; if (ioctl(ppp_sockfd, PPPIOCGFLAGS, (caddr_t) &x) < 0) { error("ioctl (PPPIOCGFLAGS): %m"); return 0; } x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP; x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID; if (ioctl(ppp_sockfd, PPPIOCSFLAGS, (caddr_t) &x) < 0) { error("ioctl(PPPIOCSFLAGS): %m"); return 0; } if (vjcomp && ioctl(ppp_sockfd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) { error("ioctl(PPPIOCSMAXCID): %m"); return 0; } publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, vjcomp); return 1; } /* ----------------------------------------------------------------------------- Config the interface up and enable IP packets to pass ----------------------------------------------------------------------------- */ int sifup(int u) { struct ifreq ifr; strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { error("ioctl (SIOCGIFFLAGS): %m"); return 0; } ifr.ifr_flags |= IFF_UP; if (ioctl(ip_sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { error("ioctl(SIOCSIFFLAGS): %m"); return 0; } if_is_up = 1; return 1; } /* ----------------------------------------------------------------------------- Set the mode for handling packets for a given NP ----------------------------------------------------------------------------- */ int sifnpmode(int u, int proto, enum NPmode mode) { struct npioctl npi; npi.protocol = proto; npi.mode = mode; if (ioctl(ppp_sockfd, PPPIOCSNPMODE, &npi) < 0) { error("ioctl(set NP %d mode to %d): %m", proto, mode); return 0; } return 1; } /* ----------------------------------------------------------------------------- Set the mode for filtering protocol addresses ----------------------------------------------------------------------------- */ int sifnpafmode(int u, int proto, enum NPAFmode mode) { struct npafioctl npi; npi.protocol = proto; npi.mode = mode; if (ioctl(ppp_sockfd, PPPIOCSNPAFMODE, &npi) < 0) { error("ioctl(set NPAF %d mode to %d): %m", proto, mode); return 0; } return 1; } /* ----------------------------------------------------------------------------- Config the interface down ----------------------------------------------------------------------------- */ int sifdown(int u) { struct ifreq ifr; int rv; struct npioctl npi; npi.protocol = PPP_IP; if ((ioctl(ppp_sockfd, PPPIOCGNPMODE, (caddr_t) &npi) == 0) && (npi.mode != NPMODE_DROP)) { return 0; } npi.protocol = PPP_IPV6; if ((ioctl(ppp_sockfd, PPPIOCGNPMODE, (caddr_t) &npi) == 0) && (npi.mode != NPMODE_DROP)) { return 0; } /* ipv4 and ipv4 are both down. take the interface down now */ rv = 1; strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { error("ioctl (SIOCGIFFLAGS): %m"); rv = 0; } else { ifr.ifr_flags &= ~IFF_UP; if (ioctl(ip_sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { error("ioctl(SIOCSIFFLAGS): %m"); rv = 0; } else if_is_up = 0; } return rv; } /* ----------------------------------------------------------------------------- set the route for the interface. ----------------------------------------------------------------------------- */ int sifroute(int u, u_int32_t o, u_int32_t h, u_int32_t m) { if (addifroute && m != 0xFFFFFFFF) { ifroute_address.s_addr = o & m; ifroute_mask.s_addr = m; ifroute_installed = route_interface(RTM_ADD, ifroute_address, ifroute_mask, IFT_PPP, ifname, 0); } return 1; } /* ----------------------------------------------------------------------------- clear the route for the interface. ----------------------------------------------------------------------------- */ int cifroute() { if (addifroute && ifroute_installed) { route_interface(RTM_DELETE, ifroute_address, ifroute_mask, IFT_PPP, ifname, 0); ifroute_installed = 0; } return 1; } /* ----------------------------------------------------------------------------- set the sa_family field of a struct sockaddr, if it exists. ----------------------------------------------------------------------------- */ #define SET_SA_FAMILY(addr, family) \ BZERO((char *) &(addr), sizeof(addr)); \ addr.sa_family = (family); \ addr.sa_len = sizeof(addr); /* ----------------------------------------------------------------------------- Config the interface IP addresses and netmask ----------------------------------------------------------------------------- */ int sifaddr(int u, u_int32_t o, u_int32_t h, u_int32_t m) { struct ifaliasreq ifra; struct ifreq ifr; // XXX from sys/sockio.h #define SIOCPROTOATTACH _IOWR('i', 80, struct ifreq) /* attach proto to interface */ #define SIOCPROTODETACH _IOWR('i', 81, struct ifreq) /* detach proto from interface */ // first plumb ip over ppp if (ipv4_plumbed == 0) { strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCPROTOATTACH, (caddr_t) &ifr) < 0) { error("Couldn't plumb IP to the interface: %d %m", errno); //return 0; } ipv4_plumbed = 1; } strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); SET_SA_FAMILY(ifra.ifra_addr, AF_INET); ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; if (m != 0) { SET_SA_FAMILY(ifra.ifra_mask, AF_INET); ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m; } else BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); BZERO(&ifr, sizeof(ifr)); strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCDIFADDR, (caddr_t) &ifr) < 0) { if (errno != EADDRNOTAVAIL) warning("Couldn't remove interface address: %m"); } if (ioctl(ip_sockfd, SIOCAIFADDR, (caddr_t) &ifra) < 0) { if (errno != EEXIST) { error("Couldn't set interface address: %m"); return 0; } warning("Couldn't set interface address: Address %I already exists", o); } ifaddrs[0] = o; ifaddrs[1] = h; if (looplocal) { struct in_addr o1; struct in_addr mask; set_flags(ppp_sockfd, get_flags(ppp_sockfd) | SC_LOOP_LOCAL); // add a route for our local address via our interface o1.s_addr = o; mask.s_addr = 0; route_interface(RTM_ADD, o1, mask, IFT_PPP, ifname, 1); } sifroute(u, o, h, m); publish_stateaddr(o, h, m); return 1; } /* ----------------------------------------------------------------------------- Clear the interface IP addresses, and delete routes * through the interface if possible ----------------------------------------------------------------------------- */ int cifaddr(int u, u_int32_t o, u_int32_t h) { //struct ifreq ifr; struct ifaliasreq ifra; // XXX from sys/sockio.h #define SIOCPROTOATTACH _IOWR('i', 80, struct ifreq) /* attach proto to interface */ #define SIOCPROTODETACH _IOWR('i', 81, struct ifreq) /* detach proto from interface */ cifroute(); ifaddrs[0] = 0; strlcpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name)); SET_SA_FAMILY(ifra.ifra_addr, AF_INET); ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o; SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET); ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h; BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask)); if (ioctl(ip_sockfd, SIOCDIFADDR, (caddr_t) &ifra) < 0) { if (errno != EADDRNOTAVAIL) warning("Couldn't delete interface address: %m"); return 0; } #if 0 // unplumb ip from ppp strlcpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl(ip_sockfd, SIOCPROTODETACH, (caddr_t) &ifr) < 0) { error("Couldn't unplumb IP from the interface: %m"); return 0; } #endif unpublish_dict(kSCEntNetIPv4); return 1; } #ifdef INET6 /* ----------------------------------------------------------------------------- Config the interface IPv6 addresses ----------------------------------------------------------------------------- */ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) { int ifindex, s; struct in6_ifreq ifr6; struct in6_aliasreq addreq6; // XXX from sys/sockio.h #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq) /* attach proto to interface */ #define SIOCPROTODETACH_IN6 _IOWR('i', 111, struct in6_ifreq) /* detach proto from interface */ #define SIOCPROTOATTACH _IOWR('i', 80, struct ifreq) /* attach proto to interface */ #define SIOCPROTODETACH _IOWR('i', 81, struct ifreq) /* detach proto from interface */ #define SIOCLL_START _IOWR('i', 130, struct in6_aliasreq) /* start aquiring linklocal on interface */ #define SIOCLL_STOP _IOWR('i', 131, struct in6_ifreq) /* deconfigure linklocal from interface */ s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { error("Can't create IPv6 socket: %m"); return 0; } /* actually, this part is not kame local - RFC2553 conformant */ ifindex = if_nametoindex(ifname); if (ifindex == 0) { error("sifaddr6: no interface %s", ifname); return 0; } strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name)); if (ioctl(s, SIOCPROTOATTACH_IN6, &ifr6) < 0) { error("sif6addr: can't attach IPv6 protocol: %m"); close(s); return 0; } memset(&addreq6, 0, sizeof(addreq6)); strlcpy(addreq6.ifra_name, ifname, sizeof(addreq6.ifra_name)); /* my addr */ addreq6.ifra_addr.sin6_family = AF_INET6; addreq6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); addreq6.ifra_addr.sin6_addr.s6_addr[0] = 0xfe; addreq6.ifra_addr.sin6_addr.s6_addr[1] = 0x80; memcpy(&addreq6.ifra_addr.sin6_addr.s6_addr[8], &our_eui64, sizeof(our_eui64)); /* KAME ifindex hack */ *(u_int16_t *)&addreq6.ifra_addr.sin6_addr.s6_addr[2] = htons(ifindex); /* his addr */ addreq6.ifra_dstaddr.sin6_family = AF_INET6; addreq6.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6); addreq6.ifra_dstaddr.sin6_addr.s6_addr[0] = 0xfe; addreq6.ifra_dstaddr.sin6_addr.s6_addr[1] = 0x80; memcpy(&addreq6.ifra_dstaddr.sin6_addr.s6_addr[8], &his_eui64, sizeof(our_eui64)); /* KAME ifindex hack */ *(u_int16_t *)&addreq6.ifra_dstaddr.sin6_addr.s6_addr[2] = htons(ifindex); /* prefix mask: 128bit */ addreq6.ifra_prefixmask.sin6_family = AF_INET6; addreq6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); memset(&addreq6.ifra_prefixmask.sin6_addr, 0xff, sizeof(addreq6.ifra_prefixmask.sin6_addr)); /* address lifetime (infty) */ addreq6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; addreq6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; if (ioctl(s, SIOCLL_START, &addreq6) < 0) { error("sif6addr: can't set LL address: %m"); close(s); return 0; } close(s); return 1; } /* ----------------------------------------------------------------------------- Clear the interface IPv6 addresses ----------------------------------------------------------------------------- */ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64) { int s; struct ifreq ifr; struct in6_ifreq ifr6; // XXX from sys/sockio.h #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq) /* attach proto to interface */ #define SIOCPROTODETACH_IN6 _IOWR('i', 111, struct in6_ifreq) /* detach proto from interface */ #define SIOCPROTOATTACH _IOWR('i', 80, struct ifreq) /* attach proto to interface */ #define SIOCPROTODETACH _IOWR('i', 81, struct ifreq) /* detach proto from interface */ #define SIOCLL_START _IOWR('i', 130, struct in6_aliasreq) /* start aquiring linklocal on interface */ #define SIOCLL_STOP _IOWR('i', 131, struct in6_ifreq) /* deconfigure linklocal from interface */ s = socket(AF_INET6, SOCK_DGRAM, 0); if (s < 0) { error("Can't create IPv6 socket: %m"); return 0; } /* first try old ioctl */ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCPROTODETACH, &ifr) < 0) { /* then new ioctl */ strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name)); if (ioctl(s, SIOCLL_STOP, &ifr6) < 0) { warning("Can't stop LL address: %m"); } strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name)); if (ioctl(s, SIOCPROTODETACH_IN6, &ifr6) < 0) { warning("Can't detach IPv6 protocol: %m"); } close(s); return 0; } close(s); return 1; } /* ----------------------------------------------------------------------------- Returns an iterator containing the primary (built-in) Ethernet interface. The caller is responsible for releasing the iterator after the caller is done with it. ----------------------------------------------------------------------------- */ static kern_return_t FindPrimaryEthernetInterfaces(io_iterator_t *matchingServices) { kern_return_t kernResult; CFMutableDictionaryRef matchingDict; CFMutableDictionaryRef propertyMatchDict; // Ethernet interfaces are instances of class kIOEthernetInterfaceClass. // IOServiceMatching is a convenience function to create a dictionary with the key kIOProviderClassKey and // the specified value. matchingDict = IOServiceMatching(kIOEthernetInterfaceClass); if (matchingDict) { // Each IONetworkInterface object has a Boolean property with the key kIOPrimaryInterface. Only the // primary (built-in) interface has this property set to TRUE. // IOServiceGetMatchingServices uses the default matching criteria defined by IOService. This considers // only the following properties plus any family-specific matching in this order of precedence // (see IOService::passiveMatch): // // kIOProviderClassKey (IOServiceMatching) // kIONameMatchKey (IOServiceNameMatching) // kIOPropertyMatchKey // kIOPathMatchKey // kIOMatchedServiceCountKey // family-specific matching // kIOBSDNameKey (IOBSDNameMatching) // kIOLocationMatchKey // The IONetworkingFamily does not define any family-specific matching. This means that in // order to have IOServiceGetMatchingServices consider the kIOPrimaryInterface property, we must // add that property to a separate dictionary and then add that to our matching dictionary // specifying kIOPropertyMatchKey. propertyMatchDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (propertyMatchDict) { // Set the value in the dictionary of the property with the given key, or add the key // to the dictionary if it doesn't exist. This call retains the value object passed in. CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); // Now add the dictionary containing the matching value for kIOPrimaryInterface to our main // matching dictionary. This call will retain propertyMatchDict, so we can release our reference // on propertyMatchDict after adding it to matchingDict. CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); CFRelease(propertyMatchDict); } } // IOServiceGetMatchingServices retains the returned iterator, so release the iterator when we're done with it. // IOServiceGetMatchingServices also consumes a reference on the matching dictionary so we don't need to release // the dictionary explicitly. kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices); return kernResult; } /* ----------------------------------------------------------------------------- Get the mac address of the primary interface ----------------------------------------------------------------------------- */ static kern_return_t GetPrimaryMACAddress(UInt8 *MACAddress) { io_object_t intfService; io_object_t controllerService; kern_return_t kernResult = KERN_FAILURE; io_iterator_t intfIterator; kernResult = FindPrimaryEthernetInterfaces(&intfIterator); if (kernResult != KERN_SUCCESS) return kernResult; // Initialize the returned address bzero(MACAddress, kIOEthernetAddressSize); // IOIteratorNext retains the returned object, so release it when we're done with it. while (intfService = IOIteratorNext(intfIterator)) { CFTypeRef MACAddressAsCFData; // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call, // since they are hardware nubs and do not participate in driver matching. In other words, // registerService() is never called on them. So we've found the IONetworkInterface and will // get its parent controller by asking for it specifically. // IORegistryEntryGetParentEntry retains the returned object, so release it when we're done with it. kernResult = IORegistryEntryGetParentEntry( intfService, kIOServicePlane, &controllerService ); if (kernResult == KERN_SUCCESS) { // Retrieve the MAC address property from the I/O Registry in the form of a CFData MACAddressAsCFData = IORegistryEntryCreateCFProperty( controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0); if (MACAddressAsCFData) { // Get the raw bytes of the MAC address from the CFData CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); CFRelease(MACAddressAsCFData); } // Done with the parent Ethernet controller object so we release it. IOObjectRelease(controllerService); } // Done with the Ethernet interface object so we release it. IOObjectRelease(intfService); } IOObjectRelease(intfIterator); return kernResult; } /* ----------------------------------------------------------------------------- Convert 48-bit Ethernet address into 64-bit EUI ----------------------------------------------------------------------------- */ int ether_to_eui64(eui64_t *p_eui64) { static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static u_int8_t allone[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; kern_return_t err; UInt8 addr[kIOEthernetAddressSize]; err = GetPrimaryMACAddress(addr); if (err != KERN_SUCCESS) { warning("Can't get hardware interface address for en0 (error = %d)\n", err); return 0; } /* check for invalid MAC address */ if (bcmp(addr, allzero, ETHER_ADDR_LEN) == 0) return 0; if (bcmp(addr, allone, ETHER_ADDR_LEN) == 0) return 0; /* And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1] */ p_eui64->e8[0] = addr[0] | 0x02; p_eui64->e8[1] = addr[1]; p_eui64->e8[2] = addr[2]; p_eui64->e8[3] = 0xFF; p_eui64->e8[4] = 0xFE; p_eui64->e8[5] = addr[3]; p_eui64->e8[6] = addr[4]; p_eui64->e8[7] = addr[5]; return 1; } #endif /* ----------------------------------------------------------------------------- assign a default route through the address given ----------------------------------------------------------------------------- */ int sifdefaultroute(int u, u_int32_t l, u_int32_t g) { return publish_dictnumentry(kSCEntNetIPv4, kSCPropNetOverridePrimary, 1); } /* ----------------------------------------------------------------------------- delete a default route through the address given ----------------------------------------------------------------------------- */ int cifdefaultroute(int u, u_int32_t l, u_int32_t g) { return unpublish_dictentry(kSCEntNetIPv4, kSCPropNetOverridePrimary); } /* ---------------------------------------------------------------------------- publish ip addresses using configd cache mechanism use new state information model ----------------------------------------------------------------------------- */ int publish_stateaddr(u_int32_t o, u_int32_t h, u_int32_t m) { struct in_addr addr; CFMutableArrayRef array; CFMutableDictionaryRef ipv4_dict; CFStringRef str; // ppp daemons without services are not published in the cache if (cfgCache == NULL) return 0; /* create the IPV4 dictionnary */ if ((ipv4_dict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) return 0; /* set the ip address src and dest arrays */ if (array = CFArrayCreateMutable(0, 1, &kCFTypeArrayCallBacks)) { addr.s_addr = o; if (str = CFStringCreateWithFormat(0, 0, CFSTR(IP_FORMAT), IP_LIST(&addr))) { CFArrayAppendValue(array, str); CFRelease(str); CFDictionarySetValue(ipv4_dict, kSCPropNetIPv4Addresses, array); } CFRelease(array); } if (array = CFArrayCreateMutable(0, 1, &kCFTypeArrayCallBacks)) { addr.s_addr = h; if (str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&addr))) { CFArrayAppendValue(array, str); CFRelease(str); CFDictionarySetValue(ipv4_dict, kSCPropNetIPv4DestAddresses, array); } CFRelease(array); } /* set the router */ addr.s_addr = h; if (str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&addr))) { CFDictionarySetValue(ipv4_dict, kSCPropNetIPv4Router, str); CFRelease(str); } /* add the interface name */ if (str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), ifname)) { CFDictionarySetValue(ipv4_dict, kSCPropInterfaceName, str); CFRelease(str); } /* update the store now */ if (str = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, kSCEntNetIPv4)) { if (SCDynamicStoreSetValue(cfgCache, str, ipv4_dict) == 0) warning("SCDynamicStoreSetValue IP %s failed: %s\n", ifname, SCErrorString(SCError())); CFRelease(str); } CFRelease(ipv4_dict); return 1; } /* ----------------------------------------------------------------------------- add a search domain, using configd cache mechanism. ----------------------------------------------------------------------------- */ int publish_dns_entry(CFStringRef property1, CFTypeRef ref1, CFStringRef property2, CFTypeRef ref2, CFStringRef property3, CFTypeRef ref3, int clean) { CFMutableArrayRef mutable_array; CFArrayRef array = NULL; CFMutableDictionaryRef dict = NULL; CFStringRef key = NULL; CFPropertyListRef ref; int ret = 0; if (cfgCache == NULL) return 0; key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, kSCEntNetDNS); if (!key) goto end; if (ref = SCDynamicStoreCopyValue(cfgCache, key)) { dict = CFDictionaryCreateMutableCopy(0, 0, ref); CFRelease(ref); } else dict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!dict || (CFGetTypeID(dict) != CFDictionaryGetTypeID())) goto end; if (!clean) array = CFDictionaryGetValue(dict, property1); if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) mutable_array = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(array) + 1, array); else mutable_array = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); if (!mutable_array) goto end; CFArrayAppendValue(mutable_array, ref1); CFDictionarySetValue(dict, property1, mutable_array); CFRelease(mutable_array); if (property2) { if (!clean) array = CFDictionaryGetValue(dict, property2); if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) mutable_array = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(array) + 1, array); else mutable_array = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); if (!mutable_array) goto end; CFArrayAppendValue(mutable_array, ref2); CFDictionarySetValue(dict, property2, mutable_array); CFRelease(mutable_array); } if (property3) { if (!clean) array = CFDictionaryGetValue(dict, property3); if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) mutable_array = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(array) + 1, array); else mutable_array = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); if (!mutable_array) goto end; CFArrayAppendValue(mutable_array, ref3); CFDictionarySetValue(dict, property3, mutable_array); CFRelease(mutable_array); } if (SCDynamicStoreSetValue(cfgCache, key, dict)) ret = 1; else warning("SCDynamicStoreSetValue DNS %s failed: %s\n", ifname, SCErrorString(SCError())); end: if (key) CFRelease(key); if (dict) CFRelease(dict); return ret; } /* ----------------------------------------------------------------------------- set dns information ----------------------------------------------------------------------------- */ int sifdns(u_int32_t dns1, u_int32_t dns2) { CFStringRef str; struct in_addr dnsaddr[2]; int result, i, clean = 1; dnsaddr[0].s_addr = dns1; dnsaddr[1].s_addr = dns2; /* warn lookupd of upcoming change */ notify_post("com.apple.system.dns.delay"); for (i = 0; i < 2; i++) { result = 0; if (dnsaddr[i].s_addr && (str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT), IP_LIST(&dnsaddr[i])))) { result = publish_dns_entry(kSCPropNetDNSServerAddresses, str, 0, 0, 0, 0, clean); CFRelease(str); clean = 0; } if (result == 0) break; } return result; } /* ----------------------------------------------------------------------------- clear dns information ----------------------------------------------------------------------------- */ int cifdns(u_int32_t dns1, u_int32_t dns2) { return unpublish_dict(kSCEntNetDNS); } static struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl hwa; char extra[128]; } arpmsg; static int arpmsg_valid; /* ----------------------------------------------------------------------------- Make a proxy ARP entry for the peer ----------------------------------------------------------------------------- */ int sifproxyarp(int unit, u_int32_t hisaddr) { int routes; /* * Get the hardware address of an interface on the same subnet * as our local address. */ memset(&arpmsg, 0, sizeof(arpmsg)); if (!get_ether_addr(hisaddr, &arpmsg.hwa)) { error("Cannot determine ethernet address for proxy ARP"); return 0; } if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { error("Couldn't add proxy arp entry: socket: %m"); return 0; } arpmsg.hdr.rtm_type = RTM_ADD; arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC; arpmsg.hdr.rtm_version = RTM_VERSION; arpmsg.hdr.rtm_seq = ++rtm_seq; arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; arpmsg.hdr.rtm_inits = RTV_EXPIRE; arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp); arpmsg.dst.sin_family = AF_INET; arpmsg.dst.sin_addr.s_addr = hisaddr; arpmsg.dst.sin_other = SIN_PROXY; arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg + arpmsg.hwa.sdl_len; if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { error("Couldn't add proxy arp entry: %m"); close(routes); return 0; } close(routes); arpmsg_valid = 1; proxy_arp_addr = hisaddr; return 1; } /* ----------------------------------------------------------------------------- Delete the proxy ARP entry for the peer ----------------------------------------------------------------------------- */ int cifproxyarp(int unit, u_int32_t hisaddr) { int routes; if (!arpmsg_valid) return 0; arpmsg_valid = 0; arpmsg.hdr.rtm_type = RTM_DELETE; arpmsg.hdr.rtm_seq = ++rtm_seq; if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { error("Couldn't delete proxy arp entry: socket: %m"); return 0; } if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) { error("Couldn't delete proxy arp entry: %m"); close(routes); return 0; } close(routes); proxy_arp_addr = 0; return 1; } #define MAX_IFS 32 /* ----------------------------------------------------------------------------- get the hardware address of an interface on the * the same subnet as ipaddr. ----------------------------------------------------------------------------- */ static int get_ether_addr(u_int32_t ipaddr, struct sockaddr_dl *hwaddr) { struct ifreq *ifr, *ifend, *ifp; u_int32_t ina, mask; struct sockaddr_dl *dla; struct ifreq ifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(ip_sockfd, SIOCGIFCONF, &ifc) < 0) { error("ioctl(SIOCGIFCONF): %m"); return 0; } /* * Scan through looking for an interface with an Internet * address on the same subnet as `ipaddr'. */ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) { if (ifr->ifr_addr.sa_family == AF_INET) { ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); /* * Check that the interface is up, and not point-to-point * or loopback. */ if (ioctl(ip_sockfd, SIOCGIFFLAGS, &ifreq) < 0) continue; if ((ifreq.ifr_flags & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST)) continue; /* * Get its netmask and check that it's on the right subnet. */ if (ioctl(ip_sockfd, SIOCGIFNETMASK, &ifreq) < 0) continue; mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr; if ((ipaddr & mask) != (ina & mask)) continue; break; } } if (ifr >= ifend) return 0; info("found interface %s for proxy arp", ifr->ifr_name); /* * Now scan through again looking for a link-level address * for this interface. */ ifp = ifr; for (ifr = ifc.ifc_req; ifr < ifend; ) { if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && ifr->ifr_addr.sa_family == AF_LINK) { /* * Found the link-level address - copy it out */ dla = (struct sockaddr_dl *) &ifr->ifr_addr; BCOPY(dla, hwaddr, dla->sdl_len); return 1; } ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len); } return 0; } /* ----------------------------------------------------------------------------- * Return user specified netmask, modified by any mask we might determine * for address `addr' (in network byte order). * Here we scan through the system's list of interfaces, looking for * any non-point-to-point interfaces which might appear to be on the same * network as `addr'. If we find any, we OR in their netmask to the * user-specified netmask. ----------------------------------------------------------------------------- */ u_int32_t GetMask(u_int32_t addr) { u_int32_t mask, nmask, ina; struct ifreq *ifr, *ifend, ifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; addr = ntohl(addr); if (IN_CLASSA(addr)) /* determine network mask for address class */ nmask = IN_CLASSA_NET; else if (IN_CLASSB(addr)) nmask = IN_CLASSB_NET; else nmask = IN_CLASSC_NET; /* class D nets are disallowed by bad_ip_adrs */ mask = netmask | htonl(nmask); /* * Scan through the system's network interfaces. */ ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(ip_sockfd, SIOCGIFCONF, &ifc) < 0) { warning("ioctl(SIOCGIFCONF): %m"); return mask; } ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len); for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) { /* * Check the interface's internet address. */ if (ifr->ifr_addr.sa_family != AF_INET) continue; ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr; if ((ntohl(ina) & nmask) != (addr & nmask)) continue; /* * Check that the interface is up, and not point-to-point or loopback. */ strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); if (ioctl(ip_sockfd, SIOCGIFFLAGS, &ifreq) < 0) continue; if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK)) != IFF_UP) continue; /* * Get its netmask and OR it into our mask. */ if (ioctl(ip_sockfd, SIOCGIFNETMASK, &ifreq) < 0) continue; mask |= ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr; } return mask; } /* ----------------------------------------------------------------------------- determine if the system has any route to * a given IP address. * For demand mode to work properly, we have to ignore routes * through our own interface. ----------------------------------------------------------------------------- */ int have_route_to(u_int32_t addr) { return -1; } /* ----------------------------------------------------------------------------- Use the hostid as part of the random number seed ----------------------------------------------------------------------------- */ int get_host_seed() { return gethostid(); } #if 0 #define LOCK_PREFIX "/var/spool/lock/LCK.." static char *lock_file; /* name of lock file created */ /* ----------------------------------------------------------------------------- create a lock file for the named lock device ----------------------------------------------------------------------------- */ int lock(char *dev) { char hdb_lock_buffer[12]; int fd, pid, n; char *p; size_t l; if ((p = strrchr(dev, '/')) != NULL) dev = p + 1; l = strlen(LOCK_PREFIX) + strlen(dev) + 1; lock_file = malloc(l); if (lock_file == NULL) novm("lock file name"); slprintf(lock_file, l, "%s%s", LOCK_PREFIX, dev); while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { if (errno == EEXIST && (fd = open(lock_file, O_RDONLY, 0)) >= 0) { /* Read the lock file to find out who has the device locked */ n = read(fd, hdb_lock_buffer, 11); if (n <= 0) { error("Can't read pid from lock file %s", lock_file); close(fd); } else { hdb_lock_buffer[n] = 0; pid = atoi(hdb_lock_buffer); if (kill(pid, 0) == -1 && errno == ESRCH) { /* pid no longer exists - remove the lock file */ if (unlink(lock_file) == 0) { close(fd); notice("Removed stale lock on %s (pid %d)", dev, pid); continue; } else warning("Couldn't remove stale lock on %s", dev); } else notice("Device %s is locked by pid %d", dev, pid); } close(fd); } else error("Can't create lock file %s: %m", lock_file); free(lock_file); lock_file = NULL; return -1; } slprintf(hdb_lock_buffer, sizeof(hdb_lock_buffer), "%10d\n", getpid()); write(fd, hdb_lock_buffer, 11); close(fd); return 0; } /* ----------------------------------------------------------------------------- remove our lockfile ----------------------------------------------------------------------------- */ void unlock() { if (lock_file) { unlink(lock_file); free(lock_file); lock_file = NULL; } } #endif /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int sys_loadplugin(char *arg) { char path[MAXPATHLEN]; int ret = -1; CFBundleRef bundle; CFURLRef url; int (*start)(CFBundleRef); if (arg[0] == '/') { strcpy(path, arg); } else { strcpy(path, "/System/Library/Extensions/"); strcat(path, arg); } url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), TRUE); if (url) { bundle = CFBundleCreate(NULL, url); if (bundle) { if (CFBundleLoadExecutable(bundle) && (start = CFBundleGetFunctionPointerForName(bundle, CFSTR("start")))) { ret = (*start)(bundle); } CFRelease(bundle); } CFRelease(url); } return ret; } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ int sys_eaploadplugin(char *arg, eap_ext *eap) { char path[MAXPATHLEN]; int ret = -1; CFBundleRef bundle; CFURLRef url; CFDictionaryRef dict; if (arg[0] == '/') { strcpy(path, arg); } else { strcpy(path, "/System/Library/Extensions/"); strcat(path, arg); } url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), TRUE); if (url) { dict = CFBundleCopyInfoDictionaryForURL(url); if (dict) { CFNumberRef num; CFStringRef str; int i; bzero(eap, sizeof(eap_ext)); num = CFDictionaryGetValue(dict, CFSTR("EAPType")); if (num && (CFGetTypeID(num) == CFNumberGetTypeID())) { CFNumberGetValue(num, kCFNumberSInt32Type, &i); eap->type = i; } str = CFDictionaryGetValue(dict, CFSTR("EAPName")); if (str && (CFGetTypeID(str) == CFStringGetTypeID())) { eap->name = malloc(CFStringGetLength(str) + 1); if (eap->name) CFStringGetCString((CFStringRef)str, eap->name, CFStringGetLength(str) + 1, kCFStringEncodingUTF8); } CFRelease(dict); bundle = CFBundleCreate(NULL, url); if (bundle) { if (CFBundleLoadExecutable(bundle)) { eap->init = CFBundleGetFunctionPointerForName(bundle, CFSTR("Init")); eap->dispose = CFBundleGetFunctionPointerForName(bundle, CFSTR("Dispose")); eap->process = CFBundleGetFunctionPointerForName(bundle, CFSTR("Process")); eap->free = CFBundleGetFunctionPointerForName(bundle, CFSTR("Free")); eap->attribute = CFBundleGetFunctionPointerForName(bundle, CFSTR("GetAttribute")); eap->interactive_ui = CFBundleGetFunctionPointerForName(bundle, CFSTR("InteractiveUI")); eap->print_packet = CFBundleGetFunctionPointerForName(bundle, CFSTR("PrintPacket")); eap->identity = CFBundleGetFunctionPointerForName(bundle, CFSTR("Identity")); // keep a ref to release later eap->plugin = bundle; ret = 0; } if (ret) CFRelease(bundle); } } CFRelease(url); } return ret; } /* ----------------------------------------------------------------------------- publish a dictionnary entry in the cache, given a key ----------------------------------------------------------------------------- */ int publish_keyentry(CFStringRef key, CFStringRef entry, CFTypeRef value) { CFMutableDictionaryRef dict; CFPropertyListRef ref; // ppp daemons without services are not published in the cache if (cfgCache == NULL) return 0; if (ref = SCDynamicStoreCopyValue(cfgCache, key)) { dict = CFDictionaryCreateMutableCopy(0, 0, ref); CFRelease(ref); } else dict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (dict == 0) return 0; CFDictionarySetValue(dict, entry, value); if (SCDynamicStoreSetValue(cfgCache, key, dict) == 0) warning("publish_entry SCDSet() failed: %s\n", SCErrorString(SCError())); CFRelease(dict); return 1; } /* ----------------------------------------------------------------------------- publish a numerical entry in the cache, given a dictionary ----------------------------------------------------------------------------- */ int publish_dictnumentry(CFStringRef dict, CFStringRef entry, int val) { int ret = ENOMEM; CFNumberRef num; CFStringRef key; key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, dict); if (key) { num = CFNumberCreate(NULL, kCFNumberIntType, &val); if (num) { ret = publish_keyentry(key, entry, num); CFRelease(num); ret = 0; } CFRelease(key); } return ret; } /* ----------------------------------------------------------------------------- publish a string entry in the cache, given a dictionary ----------------------------------------------------------------------------- */ int publish_dictstrentry(CFStringRef dict, CFStringRef entry, char *str, int encoding) { int ret = ENOMEM; CFStringRef ref; CFStringRef key; key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, dict); if (key) { ref = CFStringCreateWithCString(NULL, str, encoding); if (ref) { ret = publish_keyentry(key, entry, ref); CFRelease(ref); ret = 0; } CFRelease(key); } return ret; } /* ----------------------------------------------------------------------------- unpublish a dictionnary entry from the cache, given the dict key ----------------------------------------------------------------------------- */ int unpublish_keyentry(CFStringRef key, CFStringRef entry) { CFMutableDictionaryRef dict; CFPropertyListRef ref; // ppp daemons without services are not published in the cache if (cfgCache == NULL) return 0; if (ref = SCDynamicStoreCopyValue(cfgCache, key)) { if (dict = CFDictionaryCreateMutableCopy(0, 0, ref)) { CFDictionaryRemoveValue(dict, entry); if (SCDynamicStoreSetValue(cfgCache, key, dict) == 0) warning("unpublish_keyentry SCDSet() failed: %s\n", SCErrorString(SCError())); CFRelease(dict); } CFRelease(ref); } return 0; } /* ----------------------------------------------------------------------------- unpublish a complete dictionnary from the cache ----------------------------------------------------------------------------- */ int unpublish_dict(CFStringRef dict) { int ret = ENOMEM; CFStringRef key; if (cfgCache == NULL) return 0; key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, dict); if (key) { ret = !SCDynamicStoreRemoveValue(cfgCache, key); CFRelease(key); } return ret; } /* ----------------------------------------------------------------------------- unpublish a dictionnary entry from the cache, given the dict name ----------------------------------------------------------------------------- */ int unpublish_dictentry(CFStringRef dict, CFStringRef entry) { int ret = ENOMEM; CFStringRef key; key = SCDynamicStoreKeyCreateNetworkServiceEntity(0, kSCDynamicStoreDomainState, serviceidRef, dict); if (key) { ret = unpublish_keyentry(key, entry); CFRelease(key); ret = 0; } return ret; } /* ----------------------------------------------------------------------------- get the current logged in user ----------------------------------------------------------------------------- */ int sys_getconsoleuser(uid_t *uid) { CFStringRef str; if (str = SCDynamicStoreCopyConsoleUser(cfgCache, uid, 0)) { CFRelease(str); return 0; } return -1; } /* ----------------------------------------------------------------------------- get mach asbolute time, for timeout purpose independent of date changes ----------------------------------------------------------------------------- */ int getabsolutetime(struct timeval *timenow) { double now; if (timeScaleSeconds == 0) return -1; now = mach_absolute_time(); timenow->tv_sec = now * timeScaleSeconds; timenow->tv_usec = (now * timeScaleMicroSeconds) - ((double)timenow->tv_sec * 1000000); return 0; } /* ----------------------------------------------------------------------------- our new phase hook ----------------------------------------------------------------------------- */ void sys_phasechange(void *arg, int p) { publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPStatus, p); switch (p) { case PHASE_ESTABLISH: connecttime = mach_absolute_time() * timeScaleSeconds; publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPConnectTime, connecttime); if (maxconnect) publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPDisconnectTime, connecttime + maxconnect); break; case PHASE_SERIALCONN: unpublish_dictentry(kSCEntNetPPP, kSCPropNetPPPRetryConnectTime); break; case PHASE_WAITONBUSY: publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, redialtimer + (mach_absolute_time() * timeScaleSeconds)); break; case PHASE_RUNNING: break; case PHASE_DORMANT: case PHASE_HOLDOFF: case PHASE_DEAD: sys_eventnotify((void*)PPP_EVT_DISCONNECTED, status); break; case PHASE_DISCONNECT: unpublish_dictentry(kSCEntNetPPP, CFSTR("AuthPeerName") /*kSCPropNetPPPAuthPeerName*/); unpublish_dictentry(kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress); unpublish_dictentry(kSCEntNetPPP, kSCPropNetPPPConnectTime); unpublish_dictentry(kSCEntNetPPP, kSCPropNetPPPDisconnectTime); break; } /* send phase notification to the controller */ if (phase != PHASE_DEAD) sys_notify(PPPD_PHASE, phase, 0); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void sys_authpeersuccessnotify(void *param, int info) { struct auth_peer_success_info *peerinfo = (struct auth_peer_success_info *)info; publish_dictstrentry(kSCEntNetPPP, CFSTR("AuthPeerName") /*kSCPropNetPPPAuthPeerName*/, peerinfo->name, kCFStringEncodingUTF8); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void sys_timeremaining(void *param, int info) { struct lcp_timeremaining_info *timeinfo = (struct lcp_timeremaining_info *)info; u_int32_t val = 0, curtime = mach_absolute_time() * timeScaleSeconds; if (timeinfo->time == 0xFFFFFFFF) { // infinite timer from the server, restore our maxconnect time if any if (maxconnect) val = connecttime + maxconnect; } else { // valid timer from the server, the disconnect time will be the min between // our setup and what says the server. if (maxconnect) val = MIN(curtime + timeinfo->time, connecttime + maxconnect); else val = curtime + timeinfo->time; } if (val) publish_dictnumentry(kSCEntNetPPP, kSCPropNetPPPDisconnectTime, val); else unpublish_dictentry(kSCEntNetPPP, kSCPropNetPPPDisconnectTime); } /* ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- */ void sys_publish_remoteaddress(char *addr) { if (addr) publish_dictstrentry(kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, addr, kCFStringEncodingUTF8); } /* ----------------------------------------------------------------------------- our pid has changed, reinit things ----------------------------------------------------------------------------- */ void sys_reinit() { cfgCache = SCDynamicStoreCreate(0, CFSTR("pppd"), 0, 0); if (cfgCache == 0) fatal("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); publish_dictnumentry(kSCEntNetPPP, CFSTR("pid"), getpid()); } /* ----------------------------------------------------------------------------- we are exiting ----------------------------------------------------------------------------- */ void sys_exitnotify(void *arg, int exitcode) { // unpublish the various info about the connection unpublish_dict(kSCEntNetPPP); unpublish_dict(kSCEntNetDNS); unpublish_dict(kSCEntNetInterface); sys_eventnotify((void*)PPP_EVT_DISCONNECTED, exitcode); if (cfgCache) { CFRelease(cfgCache); cfgCache = 0; } } /* ----------------------------------------------------------------------------- add/remove a route via an interface ----------------------------------------------------------------------------- */ int route_interface(int cmd, struct in_addr host, struct in_addr addr_mask, char iftype, char *ifname, int is_host) { int len, iflen; int rtm_seq = 0; struct { struct rt_msghdr hdr; struct sockaddr_in dst; struct sockaddr_dl iface; struct sockaddr_in mask; } rtmsg; int sockfd = -1; if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { error("route_interface: open routing socket failed, %m"); return (0); } memset(&rtmsg, 0, sizeof(rtmsg)); rtmsg.hdr.rtm_type = cmd; rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC; if (is_host) rtmsg.hdr.rtm_flags |= RTF_HOST; rtmsg.hdr.rtm_version = RTM_VERSION; rtmsg.hdr.rtm_seq = ++rtm_seq; rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; rtmsg.dst.sin_len = sizeof(rtmsg.dst); rtmsg.dst.sin_family = AF_INET; rtmsg.dst.sin_addr = host; iflen = MIN(strlen(ifname), sizeof(rtmsg.iface.sdl_data)); rtmsg.iface.sdl_len = sizeof(rtmsg.iface); rtmsg.iface.sdl_family = AF_LINK; rtmsg.iface.sdl_type = iftype; rtmsg.iface.sdl_nlen = iflen; strncpy(rtmsg.iface.sdl_data, ifname, iflen); // if addr_mask non-zero add it if (addr_mask.s_addr != 0) { rtmsg.hdr.rtm_addrs |= RTA_NETMASK; rtmsg.mask.sin_len = sizeof(rtmsg.mask); rtmsg.mask.sin_family = AF_INET; rtmsg.mask.sin_addr = addr_mask; } len = sizeof(rtmsg); rtmsg.hdr.rtm_msglen = len; if (write(sockfd, &rtmsg, len) < 0) { error("route_interface: write routing socket failed, %m"); close(sockfd); return (0); } close(sockfd); return (1); } /* ----------------------------------------------------------------------------- add/remove a route via a gateway ----------------------------------------------------------------------------- */ int route_gateway(int cmd, struct in_addr dest, struct in_addr mask, struct in_addr gateway, int use_gway_flag) { int len; int rtm_seq = 0; struct { struct rt_msghdr hdr; struct sockaddr_in dst; struct sockaddr_in gway; struct sockaddr_in mask; } rtmsg; int sockfd = -1; if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { syslog(LOG_INFO, "host_gateway: open routing socket failed, %s", strerror(errno)); return (0); } memset(&rtmsg, 0, sizeof(rtmsg)); rtmsg.hdr.rtm_type = cmd; rtmsg.hdr.rtm_flags = RTF_UP | RTF_STATIC; if (use_gway_flag) rtmsg.hdr.rtm_flags |= RTF_GATEWAY; rtmsg.hdr.rtm_version = RTM_VERSION; rtmsg.hdr.rtm_seq = ++rtm_seq; rtmsg.hdr.rtm_addrs = RTA_DST | RTA_NETMASK | RTA_GATEWAY; rtmsg.dst.sin_len = sizeof(rtmsg.dst); rtmsg.dst.sin_family = AF_INET; rtmsg.dst.sin_addr = dest; rtmsg.gway.sin_len = sizeof(rtmsg.gway); rtmsg.gway.sin_family = AF_INET; rtmsg.gway.sin_addr = gateway; rtmsg.mask.sin_len = sizeof(rtmsg.mask); rtmsg.mask.sin_family = AF_INET; rtmsg.mask.sin_addr = mask; len = sizeof(rtmsg); rtmsg.hdr.rtm_msglen = len; if (write(sockfd, &rtmsg, len) < 0) { syslog(LOG_ERR, "host_gateway: write routing socket failed, %s", strerror(errno)); close(sockfd); return (0); } close(sockfd); return (1); } /* ----------------------------------------------------------------------------- return uid of the local socket file descriptor ------------------------------------------------------------------------- */ uid_t fd_local_uid(int fd) { int len; struct xucred xucred; len = sizeof(xucred); if (getsockopt(STDIN_FILENO, 0, LOCAL_PEERCRED, &xucred, &len) == 0) { return xucred.cr_uid; } return -1; }