src/socket_wrapper.c: implement recvmmsg and sendmmsg
authorStefan Metzmacher <metze@samba.org>
Mon, 16 Jan 2023 16:00:48 +0000 (17:00 +0100)
committerAndreas Schneider <asn@samba.org>
Tue, 17 Jan 2023 15:19:47 +0000 (16:19 +0100)
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
ConfigureChecks.cmake
config.h.cmake
src/socket_wrapper.c

index 2c78b83f168dabde65f1c1b20dc7c03bc0ff0b31..ccf3c12cde2ab799333f90e3ebb939574802abcb 100644 (file)
@@ -75,6 +75,8 @@ check_function_exists(pledge HAVE_PLEDGE)
 check_function_exists(_socket HAVE__SOCKET)
 check_function_exists(_close HAVE__CLOSE)
 check_function_exists(__close_nocancel HAVE___CLOSE_NOCANCEL)
+check_function_exists(recvmmsg HAVE_RECVMMSG)
+check_function_exists(sendmmsg HAVE_SENDMMSG)
 
 if (UNIX)
     find_library(DLFCN_LIBRARY dl)
@@ -147,6 +149,33 @@ if (HAVE_EVENTFD)
         HAVE_EVENTFD_UNSIGNED_INT)
 endif (HAVE_EVENTFD)
 
+if (HAVE_RECVMMSG)
+    # Linux legacy glibc < 2.21
+    set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
+    check_prototype_definition(recvmmsg
+        "int recvmmsg(int __fd, struct mmsghdr *__vmessages, unsigned int __vlen, int __flags, const struct timespec *__tmo)"
+        "-1"
+        "sys/types.h;sys/socket.h"
+        HAVE_RECVMMSG_CONST_TIMEOUT)
+    set(CMAKE_REQUIRED_DEFINITIONS)
+
+    # FreeBSD
+    check_prototype_definition(recvmmsg
+        "ssize_t recvmmsg(int __fd, struct mmsghdr * __restrict __vmessages, size_t __vlen, int __flags, const struct timespec * __restrict __tmo)"
+        "-1"
+        "sys/types.h;sys/socket.h"
+        HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT)
+endif (HAVE_RECVMMSG)
+
+if (HAVE_SENDMMSG)
+    # FreeBSD
+    check_prototype_definition(sendmmsg
+        "ssize_t sendmmsg(int __fd, struct mmsghdr * __restrict __vmessages, size_t __vlen, int __flags)"
+        "-1"
+        "sys/types.h;sys/socket.h"
+        HAVE_SENDMMSG_SSIZE_T)
+endif (HAVE_SENDMMSG)
+
 # IPV6
 check_c_source_compiles("
     #include <stdlib.h>
index 0f2fb09e58b224c13f9e6b47661dc9faf59b0525..b7d189f5aaf45fa55a70221891289ab44e4d7ab0 100644 (file)
 #cmakedefine HAVE_ACCEPT_PSOCKLEN_T 1
 #cmakedefine HAVE_IOCTL_INT 1
 #cmakedefine HAVE_EVENTFD_UNSIGNED_INT 1
+#cmakedefine HAVE_RECVMMSG 1
+#cmakedefine HAVE_RECVMMSG_CONST_TIMEOUT 1
+#cmakedefine HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT 1
+#cmakedefine HAVE_SENDMMSG 1
+#cmakedefine HAVE_SENDMMSG_SSIZE_T 1
 
 /*************************** LIBRARIES ***************************/
 
index a24a9d15a83a41838c4e644bfc659c66760f9011..90ffc25805495b62adeed4147b59f354e5d3699d 100644 (file)
@@ -537,8 +537,29 @@ typedef int (*__libc_recvfrom)(int sockfd,
                             struct sockaddr *src_addr,
                             socklen_t *addrlen);
 typedef int (*__libc_recvmsg)(int sockfd, const struct msghdr *msg, int flags);
+#ifdef HAVE_RECVMMSG
+#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT)
+/* FreeBSD */
+typedef ssize_t (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout);
+#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT)
+/* Linux legacy glibc < 2.21 */
+typedef int (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout);
+#else
+/* Linux glibc >= 2.21 */
+typedef int (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);
+#endif
+#endif /* HAVE_RECVMMSG */
 typedef int (*__libc_send)(int sockfd, const void *buf, size_t len, int flags);
 typedef int (*__libc_sendmsg)(int sockfd, const struct msghdr *msg, int flags);
+#ifdef HAVE_SENDMMSG
+#if defined(HAVE_SENDMMSG_SSIZE_T)
+/* FreeBSD */
+typedef ssize_t (*__libc_sendmmsg)(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags);
+#else
+/* Linux */
+typedef int (*__libc_sendmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags);
+#endif
+#endif /* HAVE_SENDMMSG */
 typedef int (*__libc_sendto)(int sockfd,
                           const void *buf,
                           size_t len,
@@ -605,8 +626,14 @@ struct swrap_libc_symbols {
        SWRAP_SYMBOL_ENTRY(recv);
        SWRAP_SYMBOL_ENTRY(recvfrom);
        SWRAP_SYMBOL_ENTRY(recvmsg);
+#ifdef HAVE_RECVMMSG
+       SWRAP_SYMBOL_ENTRY(recvmmsg);
+#endif
        SWRAP_SYMBOL_ENTRY(send);
        SWRAP_SYMBOL_ENTRY(sendmsg);
+#ifdef HAVE_SENDMMSG
+       SWRAP_SYMBOL_ENTRY(sendmmsg);
+#endif
        SWRAP_SYMBOL_ENTRY(sendto);
        SWRAP_SYMBOL_ENTRY(setsockopt);
 #ifdef HAVE_SIGNALFD
@@ -1131,6 +1158,24 @@ static int libc_recvmsg(int sockfd, struct msghdr *msg, int flags)
        return swrap.libc.symbols._libc_recvmsg.f(sockfd, msg, flags);
 }
 
+#ifdef HAVE_RECVMMSG
+#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT)
+/* FreeBSD */
+static ssize_t libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout)
+#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT)
+/* Linux legacy glibc < 2.21 */
+static int libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout)
+#else
+/* Linux glibc >= 2.21 */
+static int libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout)
+#endif
+{
+       swrap_bind_symbol_all();
+
+       return swrap.libc.symbols._libc_recvmmsg.f(sockfd, msgvec, vlen, flags, timeout);
+}
+#endif
+
 static int libc_send(int sockfd, const void *buf, size_t len, int flags)
 {
        swrap_bind_symbol_all();
@@ -1145,6 +1190,21 @@ static int libc_sendmsg(int sockfd, const struct msghdr *msg, int flags)
        return swrap.libc.symbols._libc_sendmsg.f(sockfd, msg, flags);
 }
 
+#ifdef HAVE_SENDMMSG
+#if defined(HAVE_SENDMMSG_SSIZE_T)
+/* FreeBSD */
+static ssize_t libc_sendmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags)
+#else
+/* Linux */
+static int libc_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags)
+#endif
+{
+       swrap_bind_symbol_all();
+
+       return swrap.libc.symbols._libc_sendmmsg.f(sockfd, msgvec, vlen, flags);
+}
+#endif
+
 static int libc_sendto(int sockfd,
                       const void *buf,
                       size_t len,
@@ -1263,8 +1323,14 @@ static void __swrap_bind_symbol_all_once(void)
        swrap_bind_symbol_libsocket(recv);
        swrap_bind_symbol_libsocket(recvfrom);
        swrap_bind_symbol_libsocket(recvmsg);
+#ifdef HAVE_RECVMMSG
+       swrap_bind_symbol_libsocket(recvmmsg);
+#endif
        swrap_bind_symbol_libsocket(send);
        swrap_bind_symbol_libsocket(sendmsg);
+#ifdef HAVE_SENDMMSG
+       swrap_bind_symbol_libsocket(sendmmsg);
+#endif
        swrap_bind_symbol_libsocket(sendto);
        swrap_bind_symbol_libsocket(setsockopt);
 #ifdef HAVE_SIGNALFD
@@ -7197,6 +7263,219 @@ ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
        return swrap_recvmsg(sockfd, msg, flags);
 }
 
+/****************************************************************************
+ *   RECVMMSG
+ ***************************************************************************/
+
+#ifdef HAVE_RECVMMSG
+#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT)
+/* FreeBSD */
+static ssize_t swrap_recvmmsg(int s, struct mmsghdr *omsgvec, size_t _vlen, int flags, const struct timespec *timeout)
+#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT)
+/* Linux legacy glibc < 2.21 */
+static int swrap_recvmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags, const struct timespec *timeout)
+#else
+/* Linux glibc >= 2.21 */
+static int swrap_recvmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags, struct timespec *timeout)
+#endif
+{
+       struct socket_info *si = find_socket_info(s);
+#define __SWRAP_RECVMMSG_MAX_VLEN 16
+       struct mmsghdr msgvec[__SWRAP_RECVMMSG_MAX_VLEN] = {};
+       struct {
+               struct iovec iov;
+               struct swrap_address from_addr;
+               struct swrap_address convert_addr;
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               size_t msg_ctrllen_filled;
+               size_t msg_ctrllen_left;
+#endif
+       } tmp[__SWRAP_RECVMMSG_MAX_VLEN] = {};
+       int vlen;
+       int i;
+       int ret;
+       int rc;
+       int saved_errno;
+
+       if (_vlen > __SWRAP_RECVMMSG_MAX_VLEN) {
+               vlen = __SWRAP_RECVMMSG_MAX_VLEN;
+       } else {
+               vlen = _vlen;
+       }
+
+       if (si == NULL) {
+               uint8_t *tmp_control[__SWRAP_RECVMMSG_MAX_VLEN] = { NULL, };
+
+               for (i = 0; i < vlen; i++) {
+                       struct msghdr *omsg = &omsgvec[i].msg_hdr;
+                       struct msghdr *msg = &msgvec[i].msg_hdr;
+
+                       rc = swrap_recvmsg_before_unix(omsg, msg,
+                                                      &tmp_control[i]);
+                       if (rc < 0) {
+                               ret = rc;
+                               goto fail_libc;
+                       }
+               }
+
+               ret = libc_recvmmsg(s, msgvec, vlen, flags, timeout);
+               if (ret < 0) {
+                       goto fail_libc;
+               }
+
+               for (i = 0; i < ret; i++) {
+                       omsgvec[i].msg_len = msgvec[i].msg_len;
+               }
+
+fail_libc:
+               saved_errno = errno;
+               for (i = 0; i < vlen; i++) {
+                       struct msghdr *omsg = &omsgvec[i].msg_hdr;
+                       struct msghdr *msg = &msgvec[i].msg_hdr;
+
+                       if (i == 0 || i < ret) {
+                               swrap_recvmsg_after_unix(msg, &tmp_control[i], omsg, ret);
+                       }
+                       SAFE_FREE(tmp_control[i]);
+               }
+               errno = saved_errno;
+
+               return ret;
+       }
+
+       for (i = 0; i < vlen; i++) {
+               struct msghdr *omsg = &omsgvec[i].msg_hdr;
+               struct msghdr *msg = &msgvec[i].msg_hdr;
+
+               tmp[i].from_addr.sa_socklen = sizeof(struct sockaddr_un);
+               tmp[i].convert_addr.sa_socklen = sizeof(struct sockaddr_storage);
+
+               msg->msg_name = &tmp[i].from_addr.sa;              /* optional address */
+               msg->msg_namelen = tmp[i].from_addr.sa_socklen;    /* size of address */
+               msg->msg_iov = omsg->msg_iov;               /* scatter/gather array */
+               msg->msg_iovlen = omsg->msg_iovlen;         /* # elements in msg_iov */
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               tmp[i].msg_ctrllen_filled = 0;
+               tmp[i].msg_ctrllen_left = omsg->msg_controllen;
+
+               msg->msg_control = omsg->msg_control;       /* ancillary data, see below */
+               msg->msg_controllen = omsg->msg_controllen; /* ancillary data buffer len */
+               msg->msg_flags = omsg->msg_flags;           /* flags on received message */
+#endif
+
+               rc = swrap_recvmsg_before(s, si, msg, &tmp[i].iov);
+               if (rc < 0) {
+                       ret = rc;
+                       goto fail_swrap;
+               }
+       }
+
+       ret = libc_recvmmsg(s, msgvec, vlen, flags, timeout);
+       if (ret < 0) {
+               goto fail_swrap;
+       }
+
+       for (i = 0; i < ret; i++) {
+               omsgvec[i].msg_len = msgvec[i].msg_len;
+       }
+
+fail_swrap:
+
+       saved_errno = errno;
+       for (i = 0; i < vlen; i++) {
+               struct msghdr *omsg = &omsgvec[i].msg_hdr;
+               struct msghdr *msg = &msgvec[i].msg_hdr;
+
+               if (!(i == 0 || i < ret)) {
+                       break;
+               }
+
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               tmp[i].msg_ctrllen_filled += msg->msg_controllen;
+               tmp[i].msg_ctrllen_left -= msg->msg_controllen;
+
+               if (omsg->msg_control != NULL) {
+                       uint8_t *p;
+
+                       p = omsg->msg_control;
+                       p += tmp[i].msg_ctrllen_filled;
+
+                       msg->msg_control = p;
+                       msg->msg_controllen = tmp[i].msg_ctrllen_left;
+               } else {
+                       msg->msg_control = NULL;
+                       msg->msg_controllen = 0;
+               }
+#endif
+
+               /*
+                * We convert the unix address to a IP address so we need a buffer
+                * which can store the address in case of SOCK_DGRAM, see below.
+                */
+               msg->msg_name = &tmp[i].convert_addr.sa;
+               msg->msg_namelen = tmp[i].convert_addr.sa_socklen;
+
+               swrap_recvmsg_after(s, si, msg,
+                                   &tmp[i].from_addr.sa.un,
+                                   tmp[i].from_addr.sa_socklen,
+                                   ret);
+
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               if (omsg->msg_control != NULL) {
+                       /* msg->msg_controllen = space left */
+                       tmp[i].msg_ctrllen_left = msg->msg_controllen;
+                       tmp[i].msg_ctrllen_filled = omsg->msg_controllen - tmp[i].msg_ctrllen_left;
+               }
+
+               /* Update the original message length */
+               omsg->msg_controllen = tmp[i].msg_ctrllen_filled;
+               omsg->msg_flags = msg->msg_flags;
+#endif
+               omsg->msg_iovlen = msg->msg_iovlen;
+
+               SWRAP_LOCK_SI(si);
+
+               /*
+                * From the manpage:
+                *
+                * The  msg_name  field  points  to a caller-allocated buffer that is
+                * used to return the source address if the socket is unconnected.  The
+                * caller should set msg_namelen to the size of this buffer before this
+                * call; upon return from a successful call, msg_name will contain the
+                * length of the returned address.  If the application  does  not  need
+                * to know the source address, msg_name can be specified as NULL.
+                */
+               if (si->type == SOCK_STREAM) {
+                       omsg->msg_namelen = 0;
+               } else if (omsg->msg_name != NULL &&
+                          omsg->msg_namelen != 0 &&
+                          omsg->msg_namelen >= msg->msg_namelen) {
+                       memcpy(omsg->msg_name, msg->msg_name, msg->msg_namelen);
+                       omsg->msg_namelen = msg->msg_namelen;
+               }
+
+               SWRAP_UNLOCK_SI(si);
+       }
+       errno = saved_errno;
+
+       return ret;
+}
+
+#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT)
+/* FreeBSD */
+ssize_t recvmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout)
+#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT)
+/* Linux legacy glibc < 2.21 */
+int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout)
+#else
+/* Linux glibc >= 2.21 */
+int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout)
+#endif
+{
+       return swrap_recvmmsg(sockfd, msgvec, vlen, flags, timeout);
+}
+#endif /* HAVE_RECVMMSG */
+
 /****************************************************************************
  *   SENDMSG
  ***************************************************************************/
@@ -7368,6 +7647,249 @@ ssize_t sendmsg(int s, const struct msghdr *omsg, int flags)
        return swrap_sendmsg(s, omsg, flags);
 }
 
+/****************************************************************************
+ *   SENDMMSG
+ ***************************************************************************/
+
+#ifdef HAVE_SENDMMSG
+#if defined(HAVE_SENDMMSG_SSIZE_T)
+/* FreeBSD */
+static ssize_t swrap_sendmmsg(int s, struct mmsghdr *omsgvec, size_t _vlen, int flags)
+#else
+/* Linux */
+static int swrap_sendmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags)
+#endif
+{
+       struct socket_info *si = find_socket_info(s);
+#define __SWRAP_SENDMMSG_MAX_VLEN 16
+       struct mmsghdr msgvec[__SWRAP_SENDMMSG_MAX_VLEN] = {};
+       struct {
+               struct iovec iov;
+               struct sockaddr_un un_addr;
+               const struct sockaddr_un *to_un;
+               const struct sockaddr *to;
+               int bcast;
+       } tmp[__SWRAP_SENDMMSG_MAX_VLEN] = {};
+       int vlen;
+       int i;
+       char *swrap_dir = NULL;
+       int connected = 0;
+       int found_bcast = 0;
+       int ret;
+       int rc;
+       int saved_errno;
+
+       if (_vlen > __SWRAP_SENDMMSG_MAX_VLEN) {
+               vlen = __SWRAP_SENDMMSG_MAX_VLEN;
+       } else {
+               vlen = _vlen;
+       }
+
+       if (!si) {
+               int scm_rights_pipe_fd[__SWRAP_SENDMMSG_MAX_VLEN];
+
+               for (i = 0; i < __SWRAP_SENDMMSG_MAX_VLEN; i++) {
+                       scm_rights_pipe_fd[i] = -1;
+               }
+
+               for (i = 0; i < vlen; i++) {
+                       struct msghdr *omsg = &omsgvec[i].msg_hdr;
+                       struct msghdr *msg = &msgvec[i].msg_hdr;
+
+                       rc = swrap_sendmsg_before_unix(omsg, msg,
+                                                      &scm_rights_pipe_fd[i]);
+                       if (rc < 0) {
+                               ret = rc;
+                               goto fail_libc;
+                       }
+               }
+
+               ret = libc_sendmmsg(s, msgvec, vlen, flags);
+               if (ret < 0) {
+                       goto fail_libc;
+               }
+
+               for (i = 0; i < ret; i++) {
+                       omsgvec[i].msg_len = msgvec[i].msg_len;
+               }
+
+fail_libc:
+               saved_errno = errno;
+               for (i = 0; i < vlen; i++) {
+                       struct msghdr *msg = &msgvec[i].msg_hdr;
+
+                       swrap_sendmsg_after_unix(msg, ret,
+                                                scm_rights_pipe_fd[i]);
+               }
+               errno = saved_errno;
+
+               return ret;
+       }
+
+       SWRAP_LOCK_SI(si);
+       connected = si->connected;
+       SWRAP_UNLOCK_SI(si);
+
+       for (i = 0; i < vlen; i++) {
+               struct msghdr *omsg = &omsgvec[i].msg_hdr;
+               struct msghdr *msg = &msgvec[i].msg_hdr;
+
+               if (connected == 0) {
+                       msg->msg_name = omsg->msg_name;             /* optional address */
+                       msg->msg_namelen = omsg->msg_namelen;       /* size of address */
+               }
+               msg->msg_iov = omsg->msg_iov;               /* scatter/gather array */
+               msg->msg_iovlen = omsg->msg_iovlen;         /* # elements in msg_iov */
+
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               if (omsg->msg_controllen > 0 && omsg->msg_control != NULL) {
+                       uint8_t *cmbuf = NULL;
+                       size_t cmlen = 0;
+
+                       rc = swrap_sendmsg_filter_cmsghdr(omsg, &cmbuf, &cmlen);
+                       if (rc < 0) {
+                               ret = rc;
+                               goto fail_swrap;
+                       }
+
+                       if (cmlen != 0) {
+                               msg->msg_control = cmbuf;
+                               msg->msg_controllen = cmlen;
+                       }
+               }
+               msg->msg_flags = omsg->msg_flags;           /* flags on received message */
+#endif
+
+               rc = swrap_sendmsg_before(s, si, msg,
+                                         &tmp[i].iov,
+                                         &tmp[i].un_addr,
+                                         &tmp[i].to_un,
+                                         &tmp[i].to,
+                                         &tmp[i].bcast);
+               if (rc < 0) {
+                       ret = rc;
+                       goto fail_swrap;
+               }
+
+               if (tmp[i].bcast) {
+                       found_bcast = 1;
+               }
+       }
+
+       if (found_bcast) {
+
+               swrap_dir = socket_wrapper_dir();
+               if (swrap_dir == NULL) {
+                       ret = -1;
+                       goto fail_swrap;
+               }
+
+               for (i = 0; i < vlen; i++) {
+                       struct msghdr *msg = &msgvec[i].msg_hdr;
+                       struct sockaddr_un *un_addr = &tmp[i].un_addr;
+                       const struct sockaddr *to = tmp[i].to;
+                       struct stat st;
+                       unsigned int iface;
+                       unsigned int prt = ntohs(((const struct sockaddr_in *)(const void *)to)->sin_port);
+                       char type;
+                       size_t l, len = 0;
+                       uint8_t *buf;
+                       off_t ofs = 0;
+                       size_t avail = 0;
+                       size_t remain;
+
+                       for (l = 0; l < (size_t)msg->msg_iovlen; l++) {
+                               avail += msg->msg_iov[l].iov_len;
+                       }
+
+                       len = avail;
+                       remain = avail;
+
+                       /* we capture it as one single packet */
+                       buf = (uint8_t *)malloc(remain);
+                       if (!buf) {
+                               ret = -1;
+                               goto fail_swrap;
+                       }
+
+                       for (l = 0; l < (size_t)msg->msg_iovlen; l++) {
+                               size_t this_time = MIN(remain, (size_t)msg->msg_iov[l].iov_len);
+                               memcpy(buf + ofs,
+                                      msg->msg_iov[l].iov_base,
+                                      this_time);
+                               ofs += this_time;
+                               remain -= this_time;
+                       }
+
+                       type = SOCKET_TYPE_CHAR_UDP;
+
+                       for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
+                               swrap_un_path(un_addr, swrap_dir, type, iface, prt);
+                               if (stat(un_addr->sun_path, &st) != 0) continue;
+
+                               msg->msg_name = un_addr;             /* optional address */
+                               msg->msg_namelen = sizeof(*un_addr); /* size of address */
+
+                               /*
+                                * ignore the any errors in broadcast sends and
+                                * do a single sendmsg instead of sendmmsg
+                                */
+                               libc_sendmsg(s, msg, flags);
+                       }
+
+                       SWRAP_LOCK_SI(si);
+                       swrap_pcap_dump_packet(si, to, SWRAP_SENDTO, buf, len);
+                       SWRAP_UNLOCK_SI(si);
+
+                       SAFE_FREE(buf);
+
+                       msgvec[i].msg_len = len;
+               }
+
+               ret = vlen;
+               goto bcast_done;
+       }
+
+       ret = libc_sendmmsg(s, msgvec, vlen, flags);
+       if (ret < 0) {
+               goto fail_swrap;
+       }
+
+bcast_done:
+       for (i = 0; i < ret; i++) {
+               omsgvec[i].msg_len = msgvec[i].msg_len;
+       }
+
+fail_swrap:
+       saved_errno = errno;
+       for (i = 0; i < vlen; i++) {
+               struct msghdr *msg = &msgvec[i].msg_hdr;
+
+               if (i == 0 || i < ret) {
+                       swrap_sendmsg_after(s, si, msg, tmp[i].to, ret);
+               }
+#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+               SAFE_FREE(msg->msg_control);
+#endif
+       }
+       SAFE_FREE(swrap_dir);
+       errno = saved_errno;
+
+       return ret;
+}
+
+#if defined(HAVE_SENDMMSG_SSIZE_T)
+/* FreeBSD */
+ssize_t sendmmsg(int s, struct mmsghdr *msgvec, size_t vlen, int flags)
+#else
+/* Linux */
+int sendmmsg(int s, struct mmsghdr *msgvec, unsigned int vlen, int flags)
+#endif
+{
+       return swrap_sendmmsg(s, msgvec, vlen, flags);
+}
+#endif /* HAVE_SENDMMSG */
+
 /****************************************************************************
  *   READV
  ***************************************************************************/