From 53389aa425e27a86059341c20ec38cbb0ad900cb Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Tue, 17 May 2016 20:26:09 +0000 Subject: [PATCH] Use DLPI to send and receive ARP and BOOTP packets. Use pfmod to filter the packets we want in the kernel. --- if-sun.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 182 insertions(+), 34 deletions(-) diff --git a/if-sun.c b/if-sun.c index 9ffbf338..9a54835d 100644 --- a/if-sun.c +++ b/if-sun.c @@ -1,6 +1,6 @@ /* - * dhcpcd - DHCP client daemon - * Copyright (c) 2006-2016 Roy Marples + * Solaris interface driver for dhcpcd + * Copyright (c) 2016 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without @@ -25,14 +25,11 @@ * SUCH DAMAGE. */ -#include -#include -#include - #include #include #include #include +#include #include #include #include @@ -43,6 +40,14 @@ #include #include +#include +#include + +#include +#include +#include +#include + #include "config.h" #include "common.h" #include "dhcp.h" @@ -52,6 +57,23 @@ #include "ipv6.h" #include "ipv6nd.h" +#ifdef INET +/* Instead of using DLPI directly, we use libdlpi which is + * Solaris sepcific. */ +struct dl_if { + TAILQ_ENTRY(dl_if) next; + struct interface *iface; + int fd; + dlpi_handle_t dh; + uint8_t broadcast[DLPI_PHYSADDR_MAX]; +}; +TAILQ_HEAD(dl_if_head, dl_if); + +struct priv { + struct dl_if_head dl_ifs; +}; +#endif + int if_init(__unused struct interface *ifp) { @@ -69,9 +91,23 @@ if_conf(__unused struct interface *ifp) int if_opensockets_os(struct dhcpcd_ctx *ctx) { +#ifdef INET + struct priv *priv; + + if ((priv = malloc(sizeof(*priv))) == NULL) + return -1; + ctx->priv = priv; + TAILQ_INIT(&priv->dl_ifs); +#else + ctx->priv = NULL; +#endif ctx->link_fd = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); +#ifdef INET + if (ctx->link_fd == -1) + free(ctx->priv); +#endif return ctx->link_fd == -1 ? -1 : 0; } @@ -79,7 +115,8 @@ void if_closesockets_os(struct dhcpcd_ctx *ctx) { - UNUSED(ctx); + /* each interface should have closed itself */ + free(ctx->priv); } int @@ -118,8 +155,8 @@ if_machinearch(char *str, size_t len) } struct linkwalk { - struct ifaddrs *lw_ifa; - int lw_error; + struct ifaddrs *lw_ifa; + int lw_error; }; static boolean_t @@ -192,8 +229,8 @@ failed1: static struct ifaddrs * if_ifa_lo0(void) { - struct ifaddrs *ifa; - struct sockaddr_dl *sdl; + struct ifaddrs *ifa; + struct sockaddr_dl *sdl; if ((ifa = calloc(1, sizeof(*ifa))) == NULL) return NULL; @@ -239,13 +276,15 @@ if_getifaddrs(struct ifaddrs **ifap) static void if_octetstr(char *buf, const Octet_t *o, ssize_t len) { - int i; - char *p; + int i; + char *p; p = buf; for (i = 0; i < o->o_length; i++) { if ((p + 1) - buf < len) *p++ = o->o_bytes[i]; + else + break; } *p = '\0'; } @@ -349,52 +388,161 @@ out: } #ifdef INET -const char *if_pfname = "SunOS"; +const char *if_pfname = "DLPI"; + +static struct dl_if * +if_findraw(struct interface *ifp, int fd) +{ + struct dl_if *di; + struct priv *priv; + + priv = (struct priv *)ifp->ctx->priv; + TAILQ_FOREACH(di, &priv->dl_ifs, next) { + if (di->fd == fd) + return di; + } + errno = ENXIO; + return NULL; +} void if_closeraw(struct interface *ifp, int fd) { + struct dl_if *di; - UNUSED(ifp); - UNUSED(fd); + if ((di = if_findraw(ifp, fd)) != NULL) { + struct priv *priv; + + priv = (struct priv *)ifp->ctx->priv; + TAILQ_REMOVE(&priv->dl_ifs, di, next); + dlpi_close(di->dh); + free(di); + } } int if_openraw(struct interface *ifp, uint16_t protocol) { + dlpi_handle_t dh; + struct priv *priv; + struct dl_if *di; + dlpi_info_t dlinfo; + struct packetfilt pf; + ushort_t *pfp; + struct strioctl sioc; - UNUSED(ifp); - UNUSED(protocol); - errno = ENOTSUP; + if (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS) + return -1; + + /* We need to register pfmod, which is similar to BPF + * so the kernel can filter out the packets we don't need. */ + if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS || + dlpi_bind(dh, protocol, NULL) != DLPI_SUCCESS || + (di = malloc(sizeof(*di))) == NULL) + goto failed1; + + di->iface = ifp; + di->dh = dh; + di->fd = dlpi_fd(dh); + memcpy(di->broadcast, dlinfo.di_bcastaddr, dlinfo.di_bcastaddrlen); + priv = (struct priv *)ifp->ctx->priv; + + pf.Pf_Priority = 0; + pfp = pf.Pf_Filter; + /* pfmod operates on 16 bits, so divide offsets by 2. + * When working on a 8 bits, mask off the bits not teested. */ + switch (protocol) { + case ETHERTYPE_IP: + /* Filter fragments. */ + *pfp++ = ENF_PUSHWORD + (offsetof(struct ip, ip_off) / 2); + *pfp++ = ENF_PUSHLIT | ENF_AND; + *pfp++ = htons(0x1fff | IP_MF); + *pfp++ = ENF_PUSHZERO | ENF_CAND; + + /* Filter UDP. */ + *pfp++ = ENF_PUSHWORD + (offsetof(struct ip, ip_p) / 2); + *pfp++ = ENF_PUSHFF00 | ENF_AND; + *pfp++ = ENF_PUSHLIT | ENF_CAND; + *pfp++ = htons(IPPROTO_UDP); + + /* Filter BOOTPC. */ + *pfp++ = ENF_PUSHWORD + + ((sizeof(struct ip) + + offsetof(struct udphdr, uh_dport)) / 2); + *pfp++ = ENF_PUSHLIT | ENF_CAND; + *pfp++ = htons(BOOTPC); + break; + + case ETHERTYPE_ARP: + /* We are only interested in IP. */ + *pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_hrd) / 2); + *pfp++ = ENF_PUSHLIT | ENF_CAND; + *pfp++ = htons(ARPHRD_ETHER); + + /* Must be REQUEST or REPLY. */ + *pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_op) / 2); + *pfp++ = ENF_PUSHLIT | ENF_CNAND; + *pfp++ = htons(ARPOP_REQUEST); + *pfp++ = ENF_PUSHWORD + (offsetof(struct arphdr, ar_op) / 2); + *pfp++ = ENF_PUSHLIT | ENF_CAND; + *pfp++ = htons(ARPOP_REPLY); + break; + + default: + errno = EPROTOTYPE; + goto failed; + } + pf.Pf_FilterLen = pfp - pf.Pf_Filter; + + sioc.ic_cmd = PFIOCSETF; + sioc.ic_timout = INFTIM; + sioc.ic_len = sizeof(pf); + sioc.ic_dp = (void *)&pf; + + /* Install the filter and then flush the stream. */ + if (ioctl(di->fd, I_PUSH, "pfmod") == -1 || + ioctl(di->fd, I_STR, &sioc) == -1 || + ioctl(di->fd, I_FLUSH, FLUSHR) == -1) + goto failed; + + TAILQ_INSERT_TAIL(&priv->dl_ifs, di, next); + return di->fd; + +failed: + free(di); +failed1: + dlpi_close(dh); return -1; } ssize_t -if_sendraw(const struct interface *ifp, int fd, uint16_t protocol, +if_sendraw(const struct interface *cifp, int fd, __unused uint16_t protocol, const void *data, size_t len) { + struct dl_if *di; + int r; + struct interface *ifp = UNCONST(cifp); - UNUSED(ifp); - UNUSED(fd); - UNUSED(protocol); - UNUSED(data); - UNUSED(len); - errno = ENOTSUP; - return -1; + if ((di = if_findraw(ifp, fd)) == NULL) + return -1; + r = dlpi_send(di->dh, di->broadcast, ifp->hwlen, data, len, NULL); + return r == DLPI_SUCCESS ? (ssize_t)len : -1; } ssize_t if_readraw(struct interface *ifp, int fd, void *data, size_t len, int *flags) { + struct dl_if *di; + int r; + size_t mlen; - UNUSED(ifp); - UNUSED(fd); - UNUSED(data); - UNUSED(len); - UNUSED(flags); - errno = ENOTSUP; - return -1; + *flags = 0; + if ((di = if_findraw(ifp, fd)) == NULL) + return -1; + mlen = len; + r = dlpi_recv(di->dh, NULL, NULL, data, &mlen, -1, NULL); + return r == DLPI_SUCCESS ? (ssize_t)mlen : -1; } int -- 2.47.3