/* * dhcpcd - DHCP client daemon * Copyright 2006-2008 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include /* Netlink suff */ #ifdef __linux__ #include /* Needed for 2.4 kernels */ #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #include #include #include #include "config.h" #include "common.h" #include "dhcp.h" #include "interface.h" #include "logger.h" void free_address (struct address_head *addresses) { address_t *p; address_t *n; if (! addresses) return; p = STAILQ_FIRST (addresses); while (p) { n = STAILQ_NEXT (p, entries); free (p); p = n; } free (addresses); } void free_route (struct route_head *routes) { route_t *p; route_t *n; if (! routes) return; p = STAILQ_FIRST (routes); while (p) { n = STAILQ_NEXT (p, entries); free (p); p = n; } free (routes); } int inet_ntocidr (struct in_addr address) { int cidr = 0; uint32_t mask = htonl (address.s_addr); while (mask) { cidr++; mask <<= 1; } return (cidr); } int inet_cidrtoaddr (int cidr, struct in_addr *addr) { int ocets; if (cidr < 0 || cidr > 32) { errno = EINVAL; return (-1); } ocets = (cidr + 7) / 8; memset (addr, 0, sizeof (*addr)); if (ocets > 0) { memset (&addr->s_addr, 255, (size_t) ocets - 1); memset ((unsigned char *) &addr->s_addr + (ocets - 1), (256 - (1 << (32 - cidr) % 8)), 1); } return (0); } uint32_t get_netmask (uint32_t addr) { uint32_t dst; if (addr == 0) return (0); dst = htonl (addr); if (IN_CLASSA (dst)) return (ntohl (IN_CLASSA_NET)); if (IN_CLASSB (dst)) return (ntohl (IN_CLASSB_NET)); if (IN_CLASSC (dst)) return (ntohl (IN_CLASSC_NET)); return (0); } char *hwaddr_ntoa (const unsigned char *hwaddr, size_t hwlen) { static char buffer[(HWADDR_LEN * 3) + 1]; char *p = buffer; size_t i; for (i = 0; i < hwlen && i < HWADDR_LEN; i++) { if (i > 0) *p ++= ':'; p += snprintf (p, 3, "%.2x", hwaddr[i]); } *p ++= '\0'; return (buffer); } size_t hwaddr_aton (unsigned char *buffer, const char *addr) { char c[3]; const char *p = addr; unsigned char *bp = buffer; size_t len = 0; c[2] = '\0'; while (*p) { c[0] = *p++; c[1] = *p++; /* Ensure that next data is EOL or a seperator with data */ if (! (*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) { errno = EINVAL; return (0); } /* Ensure that digits are hex */ if (isxdigit ((int) c[0]) == 0 || isxdigit ((int) c[1]) == 0) { errno = EINVAL; return (0); } p++; if (bp) *bp++ = (unsigned char) strtol (c, NULL, 16); else len++; } if (bp) return (bp - buffer); return (len); } static int _do_interface (const char *ifname, _unused unsigned char *hwaddr, _unused size_t *hwlen, struct in_addr *addr, bool flush, bool get) { int s; struct ifconf ifc; int retval = 0; int len = 10 * sizeof (struct ifreq); int lastlen = 0; char *p; if ((s = socket (AF_INET, SOCK_DGRAM, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return -1; } /* Not all implementations return the needed buffer size for * SIOGIFCONF so we loop like so for all until it works */ memset (&ifc, 0, sizeof (ifc)); for (;;) { ifc.ifc_len = len; ifc.ifc_buf = xmalloc ((size_t) len); if (ioctl (s, SIOCGIFCONF, &ifc) == -1) { if (errno != EINVAL || lastlen != 0) { logger (LOG_ERR, "ioctl SIOCGIFCONF: %s", strerror (errno)); close (s); free (ifc.ifc_buf); return -1; } } else { if (ifc.ifc_len == lastlen) break; lastlen = ifc.ifc_len; } free (ifc.ifc_buf); ifc.ifc_buf = NULL; len *= 2; } for (p = ifc.ifc_buf; p < ifc.ifc_buf + ifc.ifc_len;) { union { char *buffer; struct ifreq *ifr; } ifreqs; struct sockaddr_in address; struct ifreq *ifr; /* Cast the ifc buffer to an ifreq cleanly */ ifreqs.buffer = p; ifr = ifreqs.ifr; #ifdef __linux__ p += sizeof (*ifr); #else p += offsetof (struct ifreq, ifr_ifru) + ifr->ifr_addr.sa_len; #endif if (strcmp (ifname, ifr->ifr_name) != 0) continue; #ifdef AF_LINK if (hwaddr && hwlen && ifr->ifr_addr.sa_family == AF_LINK) { struct sockaddr_dl sdl; memcpy (&sdl, &ifr->ifr_addr, sizeof (sdl)); *hwlen = sdl.sdl_alen; memcpy (hwaddr, sdl.sdl_data + sdl.sdl_nlen, (size_t) sdl.sdl_alen); retval = 1; break; } #endif if (ifr->ifr_addr.sa_family == AF_INET) { memcpy (&address, &ifr->ifr_addr, sizeof (address)); if (flush) { struct sockaddr_in netmask; if (ioctl (s, SIOCGIFNETMASK, ifr) == -1) { logger (LOG_ERR, "ioctl SIOCGIFNETMASK: %s", strerror (errno)); continue; } memcpy (&netmask, &ifr->ifr_addr, sizeof (netmask)); if (del_address (ifname, address.sin_addr, netmask.sin_addr) == -1) retval = -1; } else if (get) { addr->s_addr = address.sin_addr.s_addr; retval = 1; break; } else if (address.sin_addr.s_addr == addr->s_addr) { retval = 1; break; } } } close (s); free (ifc.ifc_buf); return retval; } interface_t *read_interface (const char *ifname, _unused int metric) { int s; struct ifreq ifr; interface_t *iface = NULL; unsigned char *hwaddr = NULL; size_t hwlen = 0; sa_family_t family = 0; unsigned short mtu; #ifdef __linux__ char *p; #endif if (! ifname) return NULL; memset (&ifr, 0, sizeof (ifr)); strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if ((s = socket (AF_INET, SOCK_DGRAM, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return NULL; } #ifdef __linux__ strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl (s, SIOCGIFHWADDR, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCGIFHWADDR: %s", strerror (errno)); goto exit; } switch (ifr.ifr_hwaddr.sa_family) { case ARPHRD_ETHER: case ARPHRD_IEEE802: hwlen = ETHER_ADDR_LEN; break; case ARPHRD_IEEE1394: hwlen = EUI64_ADDR_LEN; case ARPHRD_INFINIBAND: hwlen = INFINIBAND_ADDR_LEN; break; default: logger (LOG_ERR, "interface is not Ethernet, FireWire, " \ "InfiniBand or Token Ring"); goto exit; } hwaddr = xmalloc (sizeof (unsigned char) * HWADDR_LEN); memcpy (hwaddr, ifr.ifr_hwaddr.sa_data, hwlen); family = ifr.ifr_hwaddr.sa_family; #else ifr.ifr_metric = metric; strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl (s, SIOCSIFMETRIC, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCSIFMETRIC: %s", strerror (errno)); goto exit; } hwaddr = xmalloc (sizeof (unsigned char) * HWADDR_LEN); if (_do_interface (ifname, hwaddr, &hwlen, NULL, false, false) != 1) { logger (LOG_ERR, "could not find interface %s", ifname); goto exit; } family = ARPHRD_ETHER; #endif strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl (s, SIOCGIFMTU, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror (errno)); goto exit; } if (ifr.ifr_mtu < MTU_MIN) { logger (LOG_DEBUG, "MTU of %d is too low, setting to %d", ifr.ifr_mtu, MTU_MIN); ifr.ifr_mtu = MTU_MIN; strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); if (ioctl (s, SIOCSIFMTU, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCSIFMTU,: %s", strerror (errno)); goto exit; } } mtu = ifr.ifr_mtu; strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); #ifdef __linux__ /* We can only bring the real interface up */ if ((p = strchr (ifr.ifr_name, ':'))) *p = '\0'; #endif if (ioctl (s, SIOCGIFFLAGS, &ifr) == -1) { logger (LOG_ERR, "ioctl SIOCGIFFLAGS: %s", strerror (errno)); goto exit; } if (! (ifr.ifr_flags & IFF_UP) || ! (ifr.ifr_flags & IFF_RUNNING)) { ifr.ifr_flags |= IFF_UP | IFF_RUNNING; if (ioctl (s, SIOCSIFFLAGS, &ifr) != 0) { logger (LOG_ERR, "ioctl SIOCSIFFLAGS: %s", strerror (errno)); goto exit; } } iface = xzalloc (sizeof (*iface)); strlcpy (iface->name, ifname, IF_NAMESIZE); #ifdef ENABLE_INFO snprintf (iface->infofile, PATH_MAX, INFOFILE, ifname); #endif memcpy (&iface->hwaddr, hwaddr, hwlen); iface->hwlen = hwlen; iface->family = family; iface->arpable = ! (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)); iface->mtu = iface->previous_mtu = mtu; logger (LOG_INFO, "hardware address = %s", hwaddr_ntoa (iface->hwaddr, iface->hwlen)); /* 0 is a valid fd, so init to -1 */ iface->fd = -1; #ifdef __linux__ iface->listen_fd = -1; #endif exit: close (s); free (hwaddr); return iface; } int get_mtu (const char *ifname) { struct ifreq ifr; int r; int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return (-1); } memset (&ifr, 0, sizeof (ifr)); strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); r = ioctl (s, SIOCGIFMTU, &ifr); close (s); if (r == -1) { logger (LOG_ERR, "ioctl SIOCGIFMTU: %s", strerror (errno)); return (-1); } return (ifr.ifr_mtu); } int set_mtu (const char *ifname, short int mtu) { struct ifreq ifr; int r; int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return (-1); } memset (&ifr, 0, sizeof (ifr)); logger (LOG_DEBUG, "setting MTU to %d", mtu); strlcpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); ifr.ifr_mtu = mtu; r = ioctl (s, SIOCSIFMTU, &ifr); close (s); if (r == -1) logger (LOG_ERR, "ioctl SIOCSIFMTU: %s", strerror (errno)); return (r == 0 ? 0 : -1); } static void log_route (struct in_addr destination, struct in_addr netmask, struct in_addr gateway, _unused int metric, int change, int del) { char *dstd = xstrdup (inet_ntoa (destination)); #ifdef __linux__ #define METRIC " metric %d" #else #define METRIC "" #endif if (gateway.s_addr == destination.s_addr || gateway.s_addr == INADDR_ANY) logger (LOG_INFO, "%s route to %s/%d" METRIC, change ? "changing" : del ? "removing" : "adding", dstd, inet_ntocidr (netmask) #ifdef __linux__ , metric #endif ); else if (destination.s_addr == INADDR_ANY) logger (LOG_INFO, "%s default route via %s" METRIC, change ? "changing" : del ? "removing" : "adding", inet_ntoa (gateway) #ifdef __linux__ , metric #endif ); else logger (LOG_INFO, "%s route to %s/%d via %s" METRIC, change ? "changing" : del ? "removing" : "adding", dstd, inet_ntocidr (netmask), inet_ntoa (gateway) #ifdef __linux__ , metric #endif ); free (dstd); } #if defined(BSD) || defined(__FreeBSD_kernel__) /* Darwin doesn't define this for some very odd reason */ #ifndef SA_SIZE # define SA_SIZE(sa) \ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ sizeof(long) : \ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) ) #endif static int do_address (const char *ifname, struct in_addr address, struct in_addr netmask, struct in_addr broadcast, int del) { int s; struct ifaliasreq ifa; if (! ifname) return -1; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return -1; } memset (&ifa, 0, sizeof (ifa)); strlcpy (ifa.ifra_name, ifname, sizeof (ifa.ifra_name)); #define ADDADDR(_var, _addr) { \ union { struct sockaddr *sa; struct sockaddr_in *sin; } _s; \ _s.sa = &_var; \ _s.sin->sin_family = AF_INET; \ _s.sin->sin_len = sizeof (*_s.sin); \ memcpy (&_s.sin->sin_addr, &_addr, sizeof (_s.sin->sin_addr)); \ } ADDADDR (ifa.ifra_addr, address); ADDADDR (ifa.ifra_mask, netmask); if (! del) ADDADDR (ifa.ifra_broadaddr, broadcast); #undef ADDADDR if (ioctl (s, del ? SIOCDIFADDR : SIOCAIFADDR, &ifa) == -1) { logger (LOG_ERR, "ioctl %s: %s", del ? "SIOCDIFADDR" : "SIOCAIFADDR", strerror (errno)); close (s); return -1; } close (s); return 0; } static int do_route (const char *ifname, struct in_addr destination, struct in_addr netmask, struct in_addr gateway, int metric, int change, int del) { int s; static int seq; union sockunion { struct sockaddr sa; struct sockaddr_in sin; #ifdef INET6 struct sockaddr_in6 sin6; #endif struct sockaddr_dl sdl; struct sockaddr_storage ss; } su; struct rtm { struct rt_msghdr hdr; char buffer[sizeof (su) * 3]; } rtm; char *bp = rtm.buffer; size_t l; if (! ifname) return -1; log_route (destination, netmask, gateway, metric, change, del); if ((s = socket (PF_ROUTE, SOCK_RAW, 0)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return -1; } memset (&rtm, 0, sizeof (rtm)); rtm.hdr.rtm_version = RTM_VERSION; rtm.hdr.rtm_seq = ++seq; rtm.hdr.rtm_type = change ? RTM_CHANGE : del ? RTM_DELETE : RTM_ADD; rtm.hdr.rtm_flags = RTF_UP | RTF_STATIC; /* This order is important */ rtm.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; #define ADDADDR(_addr) \ memset (&su, 0, sizeof (su)); \ su.sin.sin_family = AF_INET; \ su.sin.sin_len = sizeof (su.sin); \ memcpy (&su.sin.sin_addr, &_addr, sizeof (su.sin.sin_addr)); \ l = SA_SIZE (&(su.sa)); \ memcpy (bp, &(su), l); \ bp += l; ADDADDR (destination); if (netmask.s_addr == INADDR_BROADCAST || gateway.s_addr == INADDR_ANY) { /* Make us a link layer socket */ unsigned char *hwaddr; size_t hwlen = 0; if (netmask.s_addr == INADDR_BROADCAST) rtm.hdr.rtm_flags |= RTF_HOST; hwaddr = xmalloc (sizeof (unsigned char) * HWADDR_LEN); _do_interface (ifname, hwaddr, &hwlen, NULL, false, false); memset (&su, 0, sizeof (su)); su.sdl.sdl_len = sizeof (su.sdl); su.sdl.sdl_family = AF_LINK; su.sdl.sdl_nlen = strlen (ifname); memcpy (&su.sdl.sdl_data, ifname, (size_t) su.sdl.sdl_nlen); su.sdl.sdl_alen = hwlen; memcpy (((unsigned char *) &su.sdl.sdl_data) + su.sdl.sdl_nlen, hwaddr, (size_t) su.sdl.sdl_alen); l = SA_SIZE (&(su.sa)); memcpy (bp, &su, l); bp += l; free (hwaddr); } else { rtm.hdr.rtm_flags |= RTF_GATEWAY; ADDADDR (gateway); } ADDADDR (netmask); #undef ADDADDR rtm.hdr.rtm_msglen = l = bp - (char *)&rtm; if (write (s, &rtm, l) == -1) { /* Don't report error about routes already existing */ if (errno != EEXIST) logger (LOG_ERR, "write: %s", strerror (errno)); close (s); return -1; } close (s); return 0; } #elif __linux__ /* This netlink stuff is overly compex IMO. * The BSD implementation is much cleaner and a lot less code. * send_netlink handles the actual transmission so we can work out * if there was an error or not. */ #define BUFFERLEN 256 int send_netlink (struct nlmsghdr *hdr, netlink_callback callback, void *arg) { int s; pid_t mypid = getpid (); struct sockaddr_nl nl; struct iovec iov; struct msghdr msg; static unsigned int seq; char *buffer; ssize_t bytes; union { char *buffer; struct nlmsghdr *nlm; } h; if ((s = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); return -1; } memset (&nl, 0, sizeof (nl)); nl.nl_family = AF_NETLINK; if (bind (s, (struct sockaddr *) &nl, sizeof (nl)) == -1) { logger (LOG_ERR, "bind: %s", strerror (errno)); close (s); return -1; } memset (&iov, 0, sizeof (iov)); iov.iov_base = hdr; iov.iov_len = hdr->nlmsg_len; memset (&msg, 0, sizeof (msg)); msg.msg_name = &nl; msg.msg_namelen = sizeof (nl); msg.msg_iov = &iov; msg.msg_iovlen = 1; /* Request a reply */ hdr->nlmsg_flags |= NLM_F_ACK; hdr->nlmsg_seq = ++seq; if (sendmsg (s, &msg, 0) == -1) { logger (LOG_ERR, "write: %s", strerror (errno)); close (s); return -1; } buffer = xzalloc (sizeof (char) * BUFFERLEN); iov.iov_base = buffer; for (;;) { iov.iov_len = BUFFERLEN; bytes = recvmsg (s, &msg, 0); if (bytes == -1) { if (errno != EINTR) logger (LOG_ERR, "recvmsg: %s", strerror (errno)); continue; } if (bytes == 0) { logger (LOG_ERR, "netlink: EOF"); goto eexit; } if (msg.msg_namelen != sizeof (nl)) { logger (LOG_ERR, "netlink: sender address length mismatch"); goto eexit; } for (h.buffer = buffer; bytes >= (signed) sizeof (*h.nlm); ) { int len = h.nlm->nlmsg_len; int l = len - sizeof (*h.nlm); struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h.nlm); if (l < 0 || len > bytes) { if (msg.msg_flags & MSG_TRUNC) logger (LOG_ERR, "netlink: truncated message"); else logger (LOG_ERR, "netlink: malformed message"); goto eexit; } /* Ensure it's our message */ if (nl.nl_pid != 0 || (pid_t) h.nlm->nlmsg_pid != mypid || h.nlm->nlmsg_seq != seq) { /* Next Message */ bytes -= NLMSG_ALIGN (len); h.buffer += NLMSG_ALIGN (len); continue; } /* We get an NLMSG_ERROR back with a code of zero for success */ if (h.nlm->nlmsg_type != NLMSG_ERROR) { logger (LOG_ERR, "netlink: unexpected reply %d", h.nlm->nlmsg_type); goto eexit; } if ((unsigned) l < sizeof (*err)) { logger (LOG_ERR, "netlink: error truncated"); goto eexit; } if (err->error == 0) { int retval = 0; close (s); if (callback) { if ((retval = callback (hdr, arg)) == -1) logger (LOG_ERR, "netlink: callback failed"); } free (buffer); return (retval); } errno = -err->error; /* Don't report on something already existing */ if (errno != EEXIST) logger (LOG_ERR, "netlink: %s", strerror (errno)); goto eexit; } } eexit: close (s); free (buffer); return -1; } #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((ptrdiff_t) (nmsg)) + NLMSG_ALIGN ((nmsg)->nlmsg_len))) static int add_attr_l(struct nlmsghdr *n, unsigned int maxlen, int type, const void *data, int alen) { int len = RTA_LENGTH(alen); struct rtattr *rta; if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) { logger (LOG_ERR, "add_attr_l: message exceeded bound of %d\n", maxlen); return -1; } rta = NLMSG_TAIL (n); rta->rta_type = type; rta->rta_len = len; memcpy (RTA_DATA (rta), data, alen); n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); return 0; } static int add_attr_32(struct nlmsghdr *n, unsigned int maxlen, int type, uint32_t data) { int len = RTA_LENGTH (sizeof (data)); struct rtattr *rta; if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) { logger (LOG_ERR, "add_attr32: message exceeded bound of %d\n", maxlen); return -1; } rta = NLMSG_TAIL (n); rta->rta_type = type; rta->rta_len = len; memcpy (RTA_DATA (rta), &data, sizeof (data)); n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; return 0; } struct nlma { struct nlmsghdr hdr; struct ifaddrmsg ifa; char buffer[64]; }; struct nlmr { struct nlmsghdr hdr; struct rtmsg rt; char buffer[256]; }; static int do_address(const char *ifname, struct in_addr address, struct in_addr netmask, struct in_addr broadcast, int del) { struct nlma *nlm; int retval; if (!ifname) return -1; nlm = xzalloc (sizeof (*nlm)); nlm->hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct ifaddrmsg)); nlm->hdr.nlmsg_flags = NLM_F_REQUEST; if (! del) nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; nlm->hdr.nlmsg_type = del ? RTM_DELADDR : RTM_NEWADDR; if (! (nlm->ifa.ifa_index = if_nametoindex (ifname))) { logger (LOG_ERR, "if_nametoindex: no index for interface `%s'", ifname); free (nlm); return -1; } nlm->ifa.ifa_family = AF_INET; nlm->ifa.ifa_prefixlen = inet_ntocidr (netmask); /* This creates the aliased interface */ add_attr_l (&nlm->hdr, sizeof (*nlm), IFA_LABEL, ifname, strlen (ifname) + 1); add_attr_l (&nlm->hdr, sizeof (*nlm), IFA_LOCAL, &address.s_addr, sizeof (address.s_addr)); if (! del) add_attr_l (&nlm->hdr, sizeof (*nlm), IFA_BROADCAST, &broadcast.s_addr, sizeof (broadcast.s_addr)); retval = send_netlink (&nlm->hdr, NULL, NULL); free (nlm); return retval; } static int do_route (const char *ifname, struct in_addr destination, struct in_addr netmask, struct in_addr gateway, int metric, int change, int del) { struct nlmr *nlm; unsigned int ifindex; int retval; if (! ifname) return -1; log_route (destination, netmask, gateway, metric, change, del); if (! (ifindex = if_nametoindex (ifname))) { logger (LOG_ERR, "if_nametoindex: no index for interface `%s'", ifname); return -1; } nlm = xzalloc (sizeof (*nlm)); nlm->hdr.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); if (change) nlm->hdr.nlmsg_flags = NLM_F_REPLACE; else if (! del) nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL; nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; nlm->hdr.nlmsg_type = del ? RTM_DELROUTE : RTM_NEWROUTE; nlm->rt.rtm_family = AF_INET; nlm->rt.rtm_table = RT_TABLE_MAIN; if (del) nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; else { nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; nlm->rt.rtm_protocol = RTPROT_BOOT; if (netmask.s_addr == INADDR_BROADCAST || gateway.s_addr == INADDR_ANY) nlm->rt.rtm_scope = RT_SCOPE_LINK; else nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; nlm->rt.rtm_type = RTN_UNICAST; } nlm->rt.rtm_dst_len = inet_ntocidr (netmask); add_attr_l (&nlm->hdr, sizeof (*nlm), RTA_DST, &destination.s_addr, sizeof (destination.s_addr)); if (netmask.s_addr != INADDR_BROADCAST && destination.s_addr != gateway.s_addr) add_attr_l (&nlm->hdr, sizeof (*nlm), RTA_GATEWAY, &gateway.s_addr, sizeof (gateway.s_addr)); add_attr_32 (&nlm->hdr, sizeof (*nlm), RTA_OIF, ifindex); add_attr_32 (&nlm->hdr, sizeof (*nlm), RTA_PRIORITY, metric); retval = send_netlink (&nlm->hdr, NULL, NULL); free (nlm); return retval; } #else #error "Platform not supported!" #error "We currently support BPF and Linux sockets." #error "Other platforms may work using BPF. If yours does, please let me know" #error "so I can add it to our list." #endif int add_address (const char *ifname, struct in_addr address, struct in_addr netmask, struct in_addr broadcast) { logger (LOG_INFO, "adding IP address %s/%d", inet_ntoa (address), inet_ntocidr (netmask)); return (do_address (ifname, address, netmask, broadcast, 0)); } int del_address (const char *ifname, struct in_addr address, struct in_addr netmask) { struct in_addr t; logger (LOG_INFO, "removing IP address %s/%d", inet_ntoa (address), inet_ntocidr (netmask)); memset (&t, 0, sizeof (t)); return (do_address (ifname, address, netmask, t, 1)); } int add_route (const char *ifname, struct in_addr destination, struct in_addr netmask, struct in_addr gateway, int metric) { return (do_route (ifname, destination, netmask, gateway, metric, 0, 0)); } int change_route (const char *ifname, struct in_addr destination, struct in_addr netmask, struct in_addr gateway, int metric) { return (do_route (ifname, destination, netmask, gateway, metric, 1, 0)); } int del_route (const char *ifname, struct in_addr destination, struct in_addr netmask, struct in_addr gateway, int metric) { return (do_route (ifname, destination, netmask, gateway, metric, 0, 1)); } int flush_addresses (const char *ifname) { return (_do_interface (ifname, NULL, NULL, NULL, true, false)); } in_addr_t get_address (const char *ifname) { struct in_addr address; if (_do_interface (ifname, NULL, NULL, &address, false, true) > 0) return (address.s_addr); return (0); } int has_address (const char *ifname, struct in_addr address) { return (_do_interface (ifname, NULL, NULL, &address, false, false)); }