/*
- * dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
+ * Solaris interface driver for dhcpcd
+ * Copyright (c) 2016 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* SUCH DAMAGE.
*/
-#include <sys/ioctl.h>
-#include <sys/tihdr.h>
-#include <sys/utsname.h>
-
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <libdlpi.h>
+#include <stddef.h>
#include <stdlib.h>
#include <stropts.h>
#include <string.h>
#include <net/if_dl.h>
#include <net/if_types.h>
+#include <netinet/if_ether.h>
+#include <netinet/udp.h>
+
+#include <sys/ioctl.h>
+#include <sys/pfmod.h>
+#include <sys/tihdr.h>
+#include <sys/utsname.h>
+
#include "config.h"
#include "common.h"
#include "dhcp.h"
#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)
{
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;
}
if_closesockets_os(struct dhcpcd_ctx *ctx)
{
- UNUSED(ctx);
+ /* each interface should have closed itself */
+ free(ctx->priv);
}
int
}
struct linkwalk {
- struct ifaddrs *lw_ifa;
- int lw_error;
+ struct ifaddrs *lw_ifa;
+ int lw_error;
};
static boolean_t
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;
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';
}
}
#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