summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2012-10-30 16:41:29 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2012-10-30 16:41:29 +0000
commit44ada59129bd8071d04a44d4c39ae658e1791c82 (patch)
tree896a8aa5392a339d98d138cc1b3151b78e6f1aed /sbin
parent73b501f9e7b72dd509a9b8d60ced65be3db8742b (diff)
New file holding functions to perform direct interface and routing
maninpulations. Inspired by similar files in bgpd, dvmrpd, ldpd, etc. Necessary for imminent nuking of dhclient-script. Not linked into build at the moment.
Diffstat (limited to 'sbin')
-rw-r--r--sbin/dhclient/kroute.c638
1 files changed, 638 insertions, 0 deletions
diff --git a/sbin/dhclient/kroute.c b/sbin/dhclient/kroute.c
new file mode 100644
index 00000000000..47b7d75a9eb
--- /dev/null
+++ b/sbin/dhclient/kroute.c
@@ -0,0 +1,638 @@
+/* $OpenBSD: kroute.c,v 1.1 2012/10/30 16:41:28 krw Exp $ */
+
+/*
+ * Copyright 2012 Kenneth R Westerback <krw@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <net/if_types.h>
+#include <net/if.h>
+
+#include "dhcpd.h"
+#include "privsep.h"
+
+/*
+ * Do equivalent of
+ *
+ * route -q $rdomain -n flush -inet -iface $interface
+ * arp -dan
+ */
+void
+flush_routes_and_arp_cache(char *ifname, int rdomain)
+{
+ size_t len;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+
+ hdr.code = IMSG_FLUSH_ROUTES;
+ hdr.len = sizeof(hdr) +
+ sizeof(len) + strlen(ifname) +
+ sizeof(len) + sizeof(rdomain);
+
+ buf = buf_open(hdr.len);
+ buf_add(buf, &hdr, sizeof(hdr));
+
+ len = strlen(ifname);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, ifname, len);
+
+ len = sizeof(rdomain);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &rdomain, len);
+
+ buf_close(privfd, buf);
+}
+
+void
+priv_flush_routes_and_arp_cache(char *ifname, int rdomain)
+{
+ struct sockaddr *rti_info[RTAX_MAX];
+ int mib[7];
+ size_t needed;
+ char *lim, *buf, *next, *routelabel;
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_inarp *sin;
+ struct sockaddr_rtlabel *sa_rl;
+ int s, seqno = 0, rlen, i;
+ unsigned int ifi_index;
+
+ ifi_index = if_nametoindex(ifname);
+ if (ifi_index == 0)
+ error("No interface index found for '%s'", ifname);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ mib[6] = rdomain;
+
+ if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) {
+ if (rdomain != 0 && errno == EINVAL)
+ return;
+ error("could not get routes");
+ }
+
+ if (needed == 0)
+ return;
+
+ if ((buf = malloc(needed)) == NULL)
+ error("malloc");
+
+ if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1)
+ error("sysctl retrieval of routes: %m");
+
+ if ((s = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
+ error("opening socket to flush routes: %m");
+
+ if (asprintf(&routelabel, "DHCLIENT %d", (int)getpid()) == -1)
+ error("recreating route label: %m");
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ if (rtm->rtm_version != RTM_VERSION)
+ continue;
+
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+ if (sa->sa_family == AF_KEY)
+ continue; /* Don't flush SPD */
+
+ bzero(rti_info, sizeof(rti_info));
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (rtm->rtm_addrs & (1 << i)) {
+ rti_info[i] = sa;
+ sa = (struct sockaddr *)((char *)(sa) +
+ ROUNDUP(sa->sa_len));
+ }
+ }
+
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+
+ if (rti_info[RTAX_LABEL]) {
+ sa_rl = (struct sockaddr_rtlabel *)rti_info[RTAX_LABEL];
+ if (strcmp(routelabel, sa_rl->sr_label))
+ continue;
+ } else if (rtm->rtm_flags & RTF_LLINFO) {
+ if (rtm->rtm_flags & RTF_GATEWAY)
+ continue;
+
+ /* XXXX Check for AF_INET too? (arp ask for them) */
+ /* XXXX Need 'retry' for proxy entries? (arp does) */
+
+ sin = (struct sockaddr_inarp *)(sa);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) +
+ (char *)sin);
+
+ if (sdl->sdl_family == AF_LINK) {
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ISO88023:
+ case IFT_ISO88024:
+ case IFT_ISO88025:
+ case IFT_CARP:
+ /* Delete it. */
+ ;
+ default:
+ continue;
+ }
+ }
+ } else
+ continue;
+
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_seq = seqno;
+ rtm->rtm_tableid = rdomain;
+
+ rlen = write(s, next, rtm->rtm_msglen);
+ if (rlen == -1) {
+ if (errno != ESRCH)
+ error("RTM_DELETE write: %m");
+ } else if (rlen < (int)rtm->rtm_msglen)
+ error("short RTM_DELETE write (%d)\n", rlen);
+
+ seqno++;
+ }
+
+ close(s);
+ free(buf);
+ free(routelabel);
+}
+
+/*
+ * [priv_]add_default_route is the equivalent of
+ *
+ * route -q $rdomain -n flush -inet -iface $interface
+ *
+ * and one of
+ *
+ * route -q $rdomain add default -iface $router
+ * route -q $rdomain add default $router
+ *
+ * depending on the contents of the gateway parameter.
+ */
+void
+add_default_route(char *ifname, int rdomain, struct iaddr addr,
+ struct iaddr gateway)
+{
+ size_t len;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+
+ hdr.code = IMSG_ADD_DEFAULT_ROUTE;
+ hdr.len = sizeof(hdr) +
+ sizeof(len) + strlen(ifname) +
+ sizeof(len) + sizeof(rdomain) +
+ sizeof(len) + sizeof(addr) +
+ sizeof(len) + sizeof(gateway);
+
+ buf = buf_open(hdr.len);
+ buf_add(buf, &hdr, sizeof(hdr));
+
+ len = strlen(ifname);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, ifname, len);
+
+ len = sizeof(rdomain);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &rdomain, len);
+
+ len = sizeof(addr);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &addr, len);
+
+ len = sizeof(gateway);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &gateway, len);
+
+ buf_close(privfd, buf);
+}
+
+void
+priv_add_default_route(char *ifname, int rdomain, struct iaddr addr,
+ struct iaddr router)
+{
+ struct rt_msghdr rtm;
+ struct sockaddr_in dest, gateway, mask;
+ struct sockaddr_rtlabel label;
+ struct iovec iov[5];
+ int s, len, i, iovcnt = 0;
+
+ /*
+ * Add a default route via the specified address.
+ */
+
+ if ((s = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
+ error("Routing Socket open failed: %m");
+
+ /* Build RTM header */
+
+ bzero(&rtm, sizeof(rtm));
+
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_type = RTM_ADD;
+ rtm.rtm_tableid = rdomain;
+ rtm.rtm_priority = 0;
+ rtm.rtm_msglen = sizeof(rtm);
+
+ iov[iovcnt].iov_base = &rtm;
+ iov[iovcnt++].iov_len = sizeof(rtm);
+
+ /* Set destination address of all zeros. */
+
+ bzero(&dest, sizeof(dest));
+
+ dest.sin_len = sizeof(dest);
+ dest.sin_family = AF_INET;
+
+ rtm.rtm_addrs |= RTA_DST;
+ rtm.rtm_msglen += sizeof(dest);
+
+ iov[iovcnt].iov_base = &dest;
+ iov[iovcnt++].iov_len = sizeof(dest);
+
+ /*
+ * Set gateway address if and only if non-zero addr supplied. A
+ * gateway address of 0 implies '-iface'.
+ */
+
+ bzero(&gateway, sizeof(gateway));
+ if (bcmp(&router, &addr, sizeof(addr)) != 0) {
+ gateway.sin_len = sizeof(gateway);
+ gateway.sin_family = AF_INET;
+ gateway.sin_addr.s_addr = inet_addr(piaddr(router));
+
+ rtm.rtm_flags |= RTF_GATEWAY | RTF_STATIC;
+ rtm.rtm_addrs |= RTA_GATEWAY;
+ rtm.rtm_msglen += sizeof(gateway);
+
+ iov[iovcnt].iov_base = &gateway;
+ iov[iovcnt++].iov_len = sizeof(gateway);
+ }
+
+ /* Add netmask of 0. */
+ bzero(&mask, sizeof(mask));
+
+ mask.sin_len = sizeof(mask);
+ mask.sin_family = AF_INET;
+
+ rtm.rtm_addrs |= RTA_NETMASK;
+ rtm.rtm_msglen += sizeof(mask);
+
+ iov[iovcnt].iov_base = &mask;
+ iov[iovcnt++].iov_len = sizeof(mask);
+
+ /* Add our label so we can identify the route as our creation. */
+ bzero(&label, sizeof(label));
+ label.sr_len = sizeof(label);
+ label.sr_family = AF_UNSPEC;
+ len = snprintf(label.sr_label, sizeof(label.sr_label), "DHCLIENT %d",
+ getpid());
+ if (len == -1)
+ error("writing label for default route: %m");
+ if (len >= sizeof(label.sr_label))
+ error("label for default route too long (%zd)",
+ sizeof(label.sr_label));
+
+ rtm.rtm_addrs |= RTA_LABEL;
+ rtm.rtm_msglen += sizeof(label);
+
+ iov[iovcnt].iov_base = &label;
+ iov[iovcnt++].iov_len = sizeof(label);
+
+ /* Check for EEXIST since other dhclient may not be done. */
+ for (i = 0; i < 5; i++) {
+ if (writev(s, iov, iovcnt) != -1)
+ break;
+ if (errno != EEXIST)
+ error("failed to add default route: %m");
+ sleep(1);
+ }
+
+ close(s);
+}
+
+/*
+ * [priv_]delete_old_address is the equivalent of
+ *
+ * ifconfig <ifname> inet <addr> delete
+ * route -q <rdomain> delete <addr> 127.0.0.1
+ */
+void
+delete_old_address(char *ifname, int rdomain, struct iaddr addr)
+{
+ size_t len;
+ struct imsg_hdr hdr;
+ struct buf *buf;
+
+ /* Note the address we are deleting for RTM_DELADDR filtering! */
+ iaddr_deleting = addr;
+
+ hdr.code = IMSG_DELETE_ADDRESS;
+ hdr.len = sizeof(hdr) +
+ sizeof(len) + strlen(ifname) +
+ sizeof(len) + sizeof(rdomain) +
+ sizeof(len) + sizeof(addr);
+
+ buf = buf_open(hdr.len);
+ buf_add(buf, &hdr, sizeof(hdr));
+
+ len = strlen(ifname);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, ifname, len);
+
+ len = sizeof(rdomain);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &rdomain, len);
+
+ len = sizeof(addr);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &addr, len);
+
+ buf_close(privfd, buf);
+}
+
+void
+priv_delete_old_address(char *ifname, int rdomain, struct iaddr addr)
+{
+ struct ifaliasreq ifaliasreq;
+ struct rt_msghdr rtm;
+ struct sockaddr_in dest, gateway;
+ struct iovec iov[3];
+ struct sockaddr_in *in;
+ int s, iovcnt = 0;
+
+ /*
+ * Delete specified address on specified interface.
+ */
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ error("socket open failed: %m");
+
+ bzero(&ifaliasreq, sizeof(ifaliasreq));
+ strncpy(ifaliasreq.ifra_name, ifname, sizeof(ifaliasreq.ifra_name));
+
+ in = (struct sockaddr_in *) &ifaliasreq.ifra_addr;
+ in->sin_family = AF_INET;
+ in->sin_len = sizeof(ifaliasreq.ifra_addr);
+
+ in->sin_addr.s_addr = inet_addr(piaddr(addr));
+
+ /* SIOCDIFADDR will result in a RTM_DELADDR message we must catch! */
+ if (ioctl(s, SIOCDIFADDR, &ifaliasreq) == -1) {
+ warning("SIOCDIFADDR failed (%s): %m", piaddr(addr));
+ close(s);
+ return;
+ }
+
+ close(s);
+
+ /*
+ * Delete the 127.0.0.1 route for the specified address.
+ */
+
+ if ((s = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
+ error("Routing Socket open failed: %m");
+
+ /* Build RTM header */
+
+ bzero(&rtm, sizeof(rtm));
+
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_type = RTM_DELETE;
+ rtm.rtm_tableid = rdomain;
+ rtm.rtm_priority = 0;
+ rtm.rtm_msglen = sizeof(rtm);
+
+ iov[iovcnt].iov_base = &rtm;
+ iov[iovcnt++].iov_len = sizeof(rtm);
+
+ /* Set destination address */
+
+ bzero(&dest, sizeof(dest));
+
+ dest.sin_len = sizeof(dest);
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(piaddr(addr));
+
+ rtm.rtm_addrs |= RTA_DST;
+ rtm.rtm_msglen += sizeof(dest);
+
+ iov[iovcnt].iov_base = &dest;
+ iov[iovcnt++].iov_len = sizeof(dest);
+
+ /* Set gateway address */
+
+ bzero(&gateway, sizeof(gateway));
+
+ gateway.sin_len = sizeof(gateway);
+ gateway.sin_family = AF_INET;
+ gateway.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ rtm.rtm_flags |= RTF_GATEWAY;
+ rtm.rtm_addrs |= RTA_GATEWAY;
+ rtm.rtm_msglen += sizeof(gateway);
+
+ iov[iovcnt].iov_base = &gateway;
+ iov[iovcnt++].iov_len = sizeof(gateway);
+
+ /* ESRCH means the route does not exist to delete. */
+ if ((writev(s, iov, iovcnt) == -1) && (errno != ESRCH))
+ error("failed to delete 127.0.0.1: %m");
+}
+
+/*
+ * [priv_]add_new_address is the equivalent of
+ *
+ * ifconfig <if> inet <addr> netmask <mask> broadcast <addr>
+ * route -q <rdomain> add <addr> 127.0.0.1
+ */
+void
+add_new_address(char *ifname, int rdomain, struct iaddr addr, in_addr_t *mask)
+{
+ in_addr_t nomask;
+ struct buf *buf;
+ size_t len;
+ struct imsg_hdr hdr;
+
+ iaddr_adding = addr;
+
+ hdr.code = IMSG_ADD_ADDRESS;
+ hdr.len = sizeof(hdr) +
+ sizeof(len) + strlen(ifname) +
+ sizeof(len) + sizeof(rdomain) +
+ sizeof(len) + sizeof(addr) +
+ sizeof(len) + sizeof(*mask);
+
+ buf = buf_open(hdr.len);
+ buf_add(buf, &hdr, sizeof(hdr));
+
+ len = strlen(ifname);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, ifname, len);
+
+ len = sizeof(rdomain);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &rdomain, len);
+
+ len = sizeof(addr);
+ buf_add(buf, &len, sizeof(len));
+ buf_add(buf, &addr, len);
+
+ len = sizeof(*mask);
+ buf_add(buf, &len, sizeof(len));
+ if (mask)
+ buf_add(buf, mask, len);
+ else {
+ bzero(&nomask, sizeof(nomask));
+ buf_add(buf, &nomask, len);
+ }
+
+ buf_close(privfd, buf);
+}
+
+void
+priv_add_new_address(char *ifname, int rdomain, struct iaddr addr,
+ in_addr_t mask)
+{
+ struct ifaliasreq ifaliasreq;
+ struct rt_msghdr rtm;
+ struct sockaddr_in dest, gateway;
+ struct sockaddr_rtlabel label;
+ struct iovec iov[4];
+ struct sockaddr_in *in;
+ int s, len, i, iovcnt = 0;
+
+ /*
+ * Add specified address on specified interface.
+ */
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ error("socket open failed: %m");
+
+ bzero(&ifaliasreq, sizeof(ifaliasreq));
+ strncpy(ifaliasreq.ifra_name, ifname, sizeof(ifaliasreq.ifra_name));
+
+ /* The actual address in ifra_addr. */
+ in = (struct sockaddr_in *) &ifaliasreq.ifra_addr;
+ in->sin_family = AF_INET;
+ in->sin_len = sizeof(ifaliasreq.ifra_addr);
+ in->sin_addr.s_addr = inet_addr(piaddr(addr));
+
+ /* And the netmask in ifra_mask. */
+ in = (struct sockaddr_in *) &ifaliasreq.ifra_mask;
+ in->sin_family = AF_INET;
+ in->sin_len = sizeof(ifaliasreq.ifra_mask);
+ in->sin_addr.s_addr = mask;
+
+ /* No need to set broadcast address. Kernel can figure it out. */
+
+ if (ioctl(s, SIOCAIFADDR, &ifaliasreq) == -1)
+ warning("SIOCAIFADDR failed (%s): %m", piaddr(addr));
+
+ close(s);
+
+ /*
+ * Add the 127.0.0.1 route for the specified address.
+ */
+
+ if ((s = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
+ error("Routing Socket open failed: %m");
+
+ /* Build RTM header */
+
+ bzero(&rtm, sizeof(rtm));
+
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_type = RTM_ADD;
+ rtm.rtm_tableid = rdomain;
+ rtm.rtm_priority = 0;
+ rtm.rtm_msglen = sizeof(rtm);
+
+ iov[iovcnt].iov_base = &rtm;
+ iov[iovcnt++].iov_len = sizeof(rtm);
+
+ /* Set destination address */
+
+ bzero(&dest, sizeof(dest));
+
+ dest.sin_len = sizeof(dest);
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(piaddr(addr));
+
+ rtm.rtm_addrs |= RTA_DST;
+ rtm.rtm_msglen += sizeof(dest);
+
+ iov[iovcnt].iov_base = &dest;
+ iov[iovcnt++].iov_len = sizeof(dest);
+
+ /* Set gateway address */
+
+ bzero(&gateway, sizeof(gateway));
+
+ gateway.sin_len = sizeof(gateway);
+ gateway.sin_family = AF_INET;
+ gateway.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ rtm.rtm_flags |= RTF_GATEWAY;
+ rtm.rtm_addrs |= RTA_GATEWAY;
+ rtm.rtm_msglen += sizeof(gateway);
+
+ iov[iovcnt].iov_base = &gateway;
+ iov[iovcnt++].iov_len = sizeof(gateway);
+
+ /* Add our label so we can identify the route as our creation. */
+ bzero(&label, sizeof(label));
+
+ label.sr_len = sizeof(label);
+ label.sr_family = AF_UNSPEC;
+ len = snprintf(label.sr_label, sizeof(label.sr_label), "DHCLIENT %d",
+ getpid());
+ if (len == -1)
+ error("writing label for host route: %m");
+ if (len >= sizeof(label.sr_label))
+ error("label for host route too long (%zd)",
+ sizeof(label.sr_label));
+
+ rtm.rtm_addrs |= RTA_LABEL;
+ rtm.rtm_msglen += sizeof(label);
+
+ iov[iovcnt].iov_base = &label;
+ iov[iovcnt++].iov_len = sizeof(label);
+
+ /* Check for EEXIST since other dhclient may not be done. */
+ for (i = 0; i < 5; i++) {
+ if (writev(s, iov, iovcnt) != -1)
+ break;
+ if (errno != EEXIST)
+ error("failed to add 127.0.0.1 route: %m");
+ sleep(1);
+ }
+
+ close(s);
+}