Rather than opening / closing on demand.
This mirrors the behaviour of dhcpcd without privsep and ensures
that dhcpcd always has the resource available to do it's operations
to ensure the network stays up.
This also has the advantage of working around a recent FreeBSD-14
capsicum issue where opening a route socket in the privileged
process without capsicum fails with the same error as if it was
in capsicum.
NULL
};
-struct priv {
- int pf_inet6_fd;
-};
-
struct rtm
{
struct rt_msghdr hdr;
ps_rights_limit_fd_sockopt(ctx->link_fd);
#endif
+
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ priv->pf_link_fd = socket(PF_LINK, SOCK_DGRAM, 0);
+ if (priv->pf_link_fd == -1)
+ logerr("%s: socket(PF_LINK)", __func__);
+#endif
return 0;
}
priv = (struct priv *)ctx->priv;
if (priv->pf_inet6_fd != -1)
close(priv->pf_inet6_fd);
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ if (priv->pf_link_fd != -1)
+ close(priv->pf_link_fd);
+#endif
free(priv);
ctx->priv = NULL;
free(ctx->rt_missfilter);
static int
if_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
{
- int s;
- int retval;
+ struct priv *priv = (struct priv *)ctx->priv;
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP)
return (int)ps_root_ioctllink(ctx, req, data, len);
-#else
- UNUSED(ctx);
#endif
- s = socket(PF_LINK, SOCK_DGRAM, 0);
- if (s == -1)
- return -1;
- retval = ioctl(s, req, data, len);
- close(s);
- return retval;
+ return ioctl(priv->pf_link_fd, req, data, len);
}
#endif
(struct rtattr *)(void *)(((char *)(rta)) \
+ RTA_ALIGN((rta)->rta_len)))
-struct priv {
- int route_fd;
- int generic_fd;
- uint32_t route_pid;
-};
-
/* We need this to send a broadcast for InfiniBand.
* Our old code used sendto, but our new code writes to a raw BPF socket.
* What header structure does IPoIB use? */
if (ctx->priv != NULL) {
priv = (struct priv *)ctx->priv;
- close(priv->route_fd);
- close(priv->generic_fd);
+ if (priv->route_fd != -1)
+ close(priv->route_fd);
+ if (priv->generic_fd != -1)
+ close(priv->generic_fd);
}
}
#define COPYSA(dst, src) memcpy((dst), (src), sa_len((src)))
-struct priv {
-#ifdef INET6
- int pf_inet6_fd;
-#endif
-};
-
struct rtm
{
struct rt_msghdr hdr;
* It used to work, but lukily Solaris can fall back to
* IP_PKTINFO. */
#undef IP_RECVIF
+#endif
+
+/* Private structures specific to an OS */
+#ifdef BSD
+struct priv {
+#ifdef INET6
+ int pf_inet6_fd;
+#endif
+ int pf_link_fd; /* NetBSD only, but hard to define for here */
+};
+#endif
+#ifdef __linux__
+struct priv {
+ int route_fd;
+ int generic_fd;
+ uint32_t route_pid;
+};
+#endif
+#ifdef __sun
+struct priv {
+#ifdef INET6
+ int pf_inet6_fd;
+#endif
+};
+#endif
+#ifdef __sun
/* Solaris getifaddrs is very un-suitable for dhcpcd.
* See if-sun.c for details why. */
struct ifaddrs;
#include <unistd.h>
#include "dhcpcd.h"
+#include "if.h"
#include "logerr.h"
#include "privsep.h"
static ssize_t
-ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
+ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len)
{
- int s, err;
+#if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))
+ struct priv *priv = (struct priv *)ctx->priv;
+#endif
+ int s;
+
+ switch(domain) {
+#ifdef INET
+ case PF_INET:
+ s = ctx->pf_inet_fd;
+ break;
+#endif
+#ifdef INET6
+ case PF_INET6:
+ s = priv->pf_inet6_fd;
+ break;
+#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ case PF_LINK:
+ s = priv->pf_link_fd;
+ break;
+#endif
+ default:
+ errno = EPFNOSUPPORT;
+ return -1;
+ }
/* Only allow these ioctls */
switch(req) {
return -1;
}
- s = socket(domain, SOCK_DGRAM, 0);
- if (s == -1)
- return -1;
- err = ioctl(s, req, data, len);
- close(s);
- return err;
+ return ioctl(s, req, data, len);
}
static ssize_t
-ps_root_doroute(void *data, size_t len)
+ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- int s;
- ssize_t err;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
- if (s != -1)
- err = write(s, data, len);
- else
- err = -1;
- if (s != -1)
- close(s);
- return err;
+ return write(ctx->link_fd, data, len);
}
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
static ssize_t
-ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
+ps_root_doindirectioctl(struct dhcpcd_ctx *ctx,
+ unsigned long req, void *data, size_t len)
{
char *p = data;
struct ifreq ifr = { .ifr_flags = 0 };
memmove(data, p + IFNAMSIZ, len);
ifr.ifr_data = data;
- return ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr));
+ return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));
}
#endif
#ifdef HAVE_PLEDGE
static ssize_t
-ps_root_doifignoregroup(void *data, size_t len)
+ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- int s, err;
if (len == 0 || ((const char *)data)[len - 1] != '\0') {
errno = EINVAL;
return -1;
}
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s == -1)
- return -1;
- err = if_ignoregroup(s, data);
- close(s);
- return err;
+ return if_ignoregroup(ctx->pf_inet_fd, data);
}
#endif
#endif
ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
void **rdata, size_t *rlen, bool *free_rdata)
{
struct iovec *iov = msg->msg_iov;
switch (psm->ps_cmd) {
case PS_IOCTLLINK:
- err = ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len);
+ err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len);
break;
case PS_IOCTL6:
- err = ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len);
+ err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len);
break;
case PS_ROUTE:
- return ps_root_doroute(data, len);
+ return ps_root_doroute(ctx, data, len);
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
case PS_IOCTLINDIRECT:
- err = ps_root_doindirectioctl(psm->ps_flags, data, len);
+ err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);
break;
#endif
#ifdef HAVE_PLEDGE
case PS_IFIGNOREGRP:
- return ps_root_doifignoregroup(data, len);
+ return ps_root_doifignoregroup(ctx, data, len);
#endif
#ifdef HAVE_CAPSICUM
case PS_SYSCTL:
//#define SECCOMP_FILTER_DEBUG
static ssize_t
-ps_root_dosendnetlink(int protocol, struct msghdr *msg)
+ps_root_dosendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
{
- struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
+ struct priv *priv = (struct priv *)ctx->priv;
int s;
unsigned char buf[16 * 1024];
struct iovec riov = {
.iov_base = buf,
.iov_len = sizeof(buf),
};
- ssize_t retval;
- if ((s = if_linksocket(&snl, protocol, 0)) == -1)
+ switch(protocol) {
+ case NETLINK_GENERIC:
+ s = priv->netlink_fd;
+ break;
+ case NETLINK_ROUTE:
+ s = priv->route_fd;
+ break;
+ default:
+ errno = EPFNOSUPPORT;
return -1;
-
- if (sendmsg(s, msg, 0) == -1) {
- retval = -1;
- goto out;
}
- retval = if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
-out:
- close(s);
- return retval;
+ if (sendmsg(s, msg, 0) == -1)
+ return =-1;
+
+ return if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
}
ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
__unused void **rdata, __unused size_t *rlen, __unused bool *free_data)
{
switch (psm->ps_cmd) {
case PS_ROUTE:
- return ps_root_dosendnetlink((int)psm->ps_flags, msg);
+ return ps_root_dosendnetlink(ctx, (int)psm->ps_flags, msg);
default:
errno = ENOTSUP;
return -1;
break;
#endif
default:
- err = ps_root_os(psm, msg, &rdata, &rlen, &free_rdata);
+ err = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata);
break;
}
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
ctx->options |= DHCPCD_PRIVSEPROOT;
+ if (if_opensockets(ctx) == -1)
+ logerr("%s: if_opensockets", __func__);
+ else {
+#ifdef BSD
+ /* We only want to write to this socket, so set
+ * a small as possible buffer size. */
+ socklen_t smallbuf = 1;
+
+ if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF,
+ &smallbuf, (socklen_t)sizeof(smallbuf)) == -1)
+ logerr("%s: setsockopt(SO_RCVBUF)", __func__);
+#endif
+#ifdef __linux__
+ /* See if_opensockets_os as to why we close link_fd
+ * rather than not open it. */
+ close(ctx->link_fd);
+ ctx->link_fd = -1;
+#endif
+ }
+
/* Open network sockets for sending.
* This is a small bit wasteful for non sandboxed OS's
* but makes life very easy for unicasting DHCPv6 in non manager
int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **);
#endif
-ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *,
+ssize_t ps_root_os(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *,
void **, size_t *, bool *);
#if defined(BSD) || defined(__sun)
ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);