Hello, Linux has a recvmmsg() system call which allows to achieve several recvfrom() at a time. The patch below makes tinc use it (patch against 1.1-pre11). Basically the patch turns the handle_incoming_vpn_data variables into arrays (of size 1 when recvmmsg is not available, and thus compiled the same as before), and makes the code index into the arrays. You may want to use interdiff -w /dev/null patch to better see what changes the patch makes. With this patch, I saw the non-ciphered bandwidth achieved over direct ethernet improve from 680Mbps to 800Mbps (or conversely, reduce the CPU usage for the same bandwidth). More is yet to come: I'll have a look at extending the tun/tap interface to send/receive several packets at a time, and then also using sendmmsg will again improve performance. Samuel --- configure.ac.original 2015-10-02 17:06:31.250034677 +0200 +++ configure.ac 2015-10-02 17:06:54.147546590 +0200 @@ -187,7 +187,7 @@ dnl Checks for library functions. AC_TYPE_SIGNAL -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev], +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random recvmmsg select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev], [], [], [#include "src/have.h"] ) --- src/net_packet.c.original 2015-10-02 16:26:36.828841493 +0200 +++ src/net_packet.c 2015-10-02 17:15:05.892051503 +0200 @@ -1057,92 +1057,137 @@ return n; } +#ifdef HAVE_RECVMMSG +#define MAX_MSG 256 +#else +#define MAX_MSG 1 +#endif + void handle_incoming_vpn_data(void *data, int flags) { listen_socket_t *ls = data; - vpn_packet_t pkt; + vpn_packet_t pkt[MAX_MSG]; char *hostname; node_id_t nullid = {}; - sockaddr_t addr = {}; - socklen_t addrlen = sizeof addr; + sockaddr_t addr[MAX_MSG] = {}; +#ifdef HAVE_RECVMMSG + struct mmsghdr msg[MAX_MSG]; + struct iovec iov[MAX_MSG]; +#else + socklen_t addrlen = sizeof addr[0]; +#endif node_t *from, *to; bool direct = false; + int num = 1, i; - pkt.offset = 0; - int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen); +#ifdef HAVE_RECVMMSG + for (i = 0; i < MAX_MSG; i++) + { + pkt[i].offset = 0; + msg[i].msg_hdr.msg_name = &addr[i].sa; + msg[i].msg_hdr.msg_namelen = sizeof(addr[i]); + iov[i].iov_base = DATA(&pkt[i]); + iov[i].iov_len = MAXSIZE; + msg[i].msg_hdr.msg_iov = &iov[i]; + msg[i].msg_hdr.msg_iovlen = 1; + msg[i].msg_hdr.msg_control = NULL; + msg[i].msg_hdr.msg_controllen = 0; + } - if(len <= 0 || len > MAXSIZE) { + num = recvmmsg(ls->udp.fd, msg, MAX_MSG, MSG_DONTWAIT, NULL); +#else + pkt[0].offset = 0; + int len = recvfrom(ls->udp.fd, DATA(&pkt[0]), MAXSIZE, 0, &addr[0].sa, &addrlen); +#endif + +#ifdef HAVE_RECVMMSG + if(num < 0) +#else + if(len <= 0 || len > MAXSIZE) +#endif + { if(!sockwouldblock(sockerrno)) logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno)); return; } - pkt.len = len; - - sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */ - - // Try to figure out who sent this packet. - - node_t *n = lookup_node_udp(&addr); - - if(!n) { - // It might be from a 1.1 node, which might have a source ID in the packet. - pkt.offset = 2 * sizeof(node_id_t); - from = lookup_node_id(SRCID(&pkt)); - if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) { - if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t))) - n = from; - else - goto skip_harder; +#ifndef HAVE_RECVMMSG + pkt[0].len = len; +#endif + + for (i = 0; i < num; i++) + { +#ifdef HAVE_RECVMMSG + pkt[i].len = msg[i].msg_len; + if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE) + continue; +#endif + + sockaddrunmap(&addr[i]); /* Some braindead IPv6 implementations do stupid things. */ + + // Try to figure out who sent this packet. + + node_t *n = lookup_node_udp(&addr[i]); + + if(!n) { + // It might be from a 1.1 node, which might have a source ID in the packet. + pkt[i].offset = 2 * sizeof(node_id_t); + from = lookup_node_id(SRCID(&pkt[i])); + if(from && !memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid) && from->status.sptps) { + if(sptps_verify_datagram(&from->sptps, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t))) + n = from; + else + goto skip_harder; + } } - } - if(!n) { - pkt.offset = 0; - n = try_harder(&addr, &pkt); - } + if(!n) { + pkt[i].offset = 0; + n = try_harder(&addr[i], &pkt[i]); + } -skip_harder: - if(!n) { - if(debug_level >= DEBUG_PROTOCOL) { - hostname = sockaddr2hostname(&addr); - logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname); - free(hostname); + skip_harder: + if(!n) { + if(debug_level >= DEBUG_PROTOCOL) { + hostname = sockaddr2hostname(&addr[i]); + logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname); + free(hostname); + } + continue; } - return; - } - if(n->status.sptps) { - pkt.offset = 2 * sizeof(node_id_t); + if(n->status.sptps) { + pkt[i].offset = 2 * sizeof(node_id_t); - if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) { + if(!memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid)) { + direct = true; + from = n; + to = myself; + } else { + from = lookup_node_id(SRCID(&pkt[i])); + to = lookup_node_id(DSTID(&pkt[i])); + } + if(!from || !to) { + logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname); + continue; + } + + if(to != myself) { + send_sptps_data_priv(to, n, 0, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t)); + continue; + } + } else { direct = true; from = n; - to = myself; - } else { - from = lookup_node_id(SRCID(&pkt)); - to = lookup_node_id(DSTID(&pkt)); - } - if(!from || !to) { - logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname); - return; } - if(to != myself) { - send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)); - return; - } - } else { - direct = true; - from = n; + pkt[i].offset = 0; + if(!receive_udppacket(from, &pkt[i])) + continue; + + n->sock = ls - listen_socket; + if(direct && sockaddrcmp(&addr[i], &n->address)) + update_node_udp(n, &addr[i]); } - - pkt.offset = 0; - if(!receive_udppacket(from, &pkt)) - return; - - n->sock = ls - listen_socket; - if(direct && sockaddrcmp(&addr, &n->address)) - update_node_udp(n, &addr); } void handle_device_data(void *data, int flags) {
Oh, goodie! I'd made a start on the send direction here: https://github.com/dtaht/tinc/commits/master Perhaps that will help. I would not just pull that as it was just some late night hacking... and it turns out the posix time calls I upgraded to to get better than second resolution are not supported on OSX. My ultimate intent was to move to not bottlenecking or dropping packets on the recv buffer ever (but I figured recvmmsg AND threads would be needed), and move tinc queue management to an fq_codel like system, so as to remove apparent network delays when it was bottlenecked on crypto or output. There are also some notes there on how to do tos encapsulation more right in the upcoming ecn'd world.... and I think I discussed here the prospect of doing a fully fq'd vpn utilizing an entire ipv6 /64.... Dave T?ht Let's go make home routers and wifi faster! With better software! https://www.gofundme.com/savewifi On Wed, Dec 2, 2015 at 12:58 PM, Samuel Thibault <samuel.thibault at ens-lyon.org> wrote:> Hello, > > Linux has a recvmmsg() system call which allows to achieve several > recvfrom() at a time. The patch below makes tinc use it (patch against > 1.1-pre11). Basically the patch turns the handle_incoming_vpn_data > variables into arrays (of size 1 when recvmmsg is not available, and > thus compiled the same as before), and makes the code index into the > arrays. You may want to use interdiff -w /dev/null patch to better see > what changes the patch makes. > > With this patch, I saw the non-ciphered bandwidth achieved over direct > ethernet improve from 680Mbps to 800Mbps (or conversely, reduce the CPU > usage for the same bandwidth). > > More is yet to come: I'll have a look at extending the tun/tap interface > to send/receive several packets at a time, and then also using sendmmsg > will again improve performance. > > Samuel > > --- configure.ac.original 2015-10-02 17:06:31.250034677 +0200 > +++ configure.ac 2015-10-02 17:06:54.147546590 +0200 > @@ -187,7 +187,7 @@ > > dnl Checks for library functions. > AC_TYPE_SIGNAL > -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev], > +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall putenv random recvmmsg select strdup strerror strsignal strtol system time usleep unsetenv vsyslog writev], > [], [], [#include "src/have.h"] > ) > > --- src/net_packet.c.original 2015-10-02 16:26:36.828841493 +0200 > +++ src/net_packet.c 2015-10-02 17:15:05.892051503 +0200 > @@ -1057,92 +1057,137 @@ > return n; > } > > +#ifdef HAVE_RECVMMSG > +#define MAX_MSG 256 > +#else > +#define MAX_MSG 1 > +#endif > + > void handle_incoming_vpn_data(void *data, int flags) { > listen_socket_t *ls = data; > - vpn_packet_t pkt; > + vpn_packet_t pkt[MAX_MSG]; > char *hostname; > node_id_t nullid = {}; > - sockaddr_t addr = {}; > - socklen_t addrlen = sizeof addr; > + sockaddr_t addr[MAX_MSG] = {}; > +#ifdef HAVE_RECVMMSG > + struct mmsghdr msg[MAX_MSG]; > + struct iovec iov[MAX_MSG]; > +#else > + socklen_t addrlen = sizeof addr[0]; > +#endif > node_t *from, *to; > bool direct = false; > + int num = 1, i; > > - pkt.offset = 0; > - int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen); > +#ifdef HAVE_RECVMMSG > + for (i = 0; i < MAX_MSG; i++) > + { > + pkt[i].offset = 0; > + msg[i].msg_hdr.msg_name = &addr[i].sa; > + msg[i].msg_hdr.msg_namelen = sizeof(addr[i]); > + iov[i].iov_base = DATA(&pkt[i]); > + iov[i].iov_len = MAXSIZE; > + msg[i].msg_hdr.msg_iov = &iov[i]; > + msg[i].msg_hdr.msg_iovlen = 1; > + msg[i].msg_hdr.msg_control = NULL; > + msg[i].msg_hdr.msg_controllen = 0; > + } > > - if(len <= 0 || len > MAXSIZE) { > + num = recvmmsg(ls->udp.fd, msg, MAX_MSG, MSG_DONTWAIT, NULL); > +#else > + pkt[0].offset = 0; > + int len = recvfrom(ls->udp.fd, DATA(&pkt[0]), MAXSIZE, 0, &addr[0].sa, &addrlen); > +#endif > + > +#ifdef HAVE_RECVMMSG > + if(num < 0) > +#else > + if(len <= 0 || len > MAXSIZE) > +#endif > + { > if(!sockwouldblock(sockerrno)) > logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno)); > return; > } > > - pkt.len = len; > - > - sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */ > - > - // Try to figure out who sent this packet. > - > - node_t *n = lookup_node_udp(&addr); > - > - if(!n) { > - // It might be from a 1.1 node, which might have a source ID in the packet. > - pkt.offset = 2 * sizeof(node_id_t); > - from = lookup_node_id(SRCID(&pkt)); > - if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) { > - if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t))) > - n = from; > - else > - goto skip_harder; > +#ifndef HAVE_RECVMMSG > + pkt[0].len = len; > +#endif > + > + for (i = 0; i < num; i++) > + { > +#ifdef HAVE_RECVMMSG > + pkt[i].len = msg[i].msg_len; > + if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE) > + continue; > +#endif > + > + sockaddrunmap(&addr[i]); /* Some braindead IPv6 implementations do stupid things. */ > + > + // Try to figure out who sent this packet. > + > + node_t *n = lookup_node_udp(&addr[i]); > + > + if(!n) { > + // It might be from a 1.1 node, which might have a source ID in the packet. > + pkt[i].offset = 2 * sizeof(node_id_t); > + from = lookup_node_id(SRCID(&pkt[i])); > + if(from && !memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid) && from->status.sptps) { > + if(sptps_verify_datagram(&from->sptps, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t))) > + n = from; > + else > + goto skip_harder; > + } > } > - } > > - if(!n) { > - pkt.offset = 0; > - n = try_harder(&addr, &pkt); > - } > + if(!n) { > + pkt[i].offset = 0; > + n = try_harder(&addr[i], &pkt[i]); > + } > > -skip_harder: > - if(!n) { > - if(debug_level >= DEBUG_PROTOCOL) { > - hostname = sockaddr2hostname(&addr); > - logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname); > - free(hostname); > + skip_harder: > + if(!n) { > + if(debug_level >= DEBUG_PROTOCOL) { > + hostname = sockaddr2hostname(&addr[i]); > + logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname); > + free(hostname); > + } > + continue; > } > - return; > - } > > - if(n->status.sptps) { > - pkt.offset = 2 * sizeof(node_id_t); > + if(n->status.sptps) { > + pkt[i].offset = 2 * sizeof(node_id_t); > > - if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) { > + if(!memcmp(DSTID(&pkt[i]), &nullid, sizeof nullid)) { > + direct = true; > + from = n; > + to = myself; > + } else { > + from = lookup_node_id(SRCID(&pkt[i])); > + to = lookup_node_id(DSTID(&pkt[i])); > + } > + if(!from || !to) { > + logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname); > + continue; > + } > + > + if(to != myself) { > + send_sptps_data_priv(to, n, 0, DATA(&pkt[i]), pkt[i].len - 2 * sizeof(node_id_t)); > + continue; > + } > + } else { > direct = true; > from = n; > - to = myself; > - } else { > - from = lookup_node_id(SRCID(&pkt)); > - to = lookup_node_id(DSTID(&pkt)); > - } > - if(!from || !to) { > - logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname); > - return; > } > > - if(to != myself) { > - send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)); > - return; > - } > - } else { > - direct = true; > - from = n; > + pkt[i].offset = 0; > + if(!receive_udppacket(from, &pkt[i])) > + continue; > + > + n->sock = ls - listen_socket; > + if(direct && sockaddrcmp(&addr[i], &n->address)) > + update_node_udp(n, &addr[i]); > } > - > - pkt.offset = 0; > - if(!receive_udppacket(from, &pkt)) > - return; > - > - n->sock = ls - listen_socket; > - if(direct && sockaddrcmp(&addr, &n->address)) > - update_node_udp(n, &addr); > } > > void handle_device_data(void *data, int flags) { > > _______________________________________________ > tinc-devel mailing list > tinc-devel at tinc-vpn.org > http://www.tinc-vpn.org/cgi-bin/mailman/listinfo/tinc-devel
Hello, Dave Taht, on Wed 02 Dec 2015 13:21:56 +0100, wrote:> I'd made a start on the send direction here: > https://github.com/dtaht/tinc/commits/master > > Perhaps that will help.Well, converting a sendto call into a sendmsg call is not really "hard" :) What will be really hard is getting multiple packet receive/send over the tap/tun device, because support for it has to be added at the kernel layer first. At least for now we could commit the recvmmsg part? Samuel
On Wed, Dec 02, 2015 at 12:58:26PM +0100, Samuel Thibault wrote:> Linux has a recvmmsg() system call which allows to achieve several > recvfrom() at a time. The patch below makes tinc use it (patch against > 1.1-pre11). Basically the patch turns the handle_incoming_vpn_data > variables into arrays (of size 1 when recvmmsg is not available, and > thus compiled the same as before), and makes the code index into the > arrays. You may want to use interdiff -w /dev/null patch to better see > what changes the patch makes. > > With this patch, I saw the non-ciphered bandwidth achieved over direct > ethernet improve from 680Mbps to 800Mbps (or conversely, reduce the CPU > usage for the same bandwidth).That's great! It would be good though to split handle_incoming_vpn_data() into a function that does the actual recvfrom/mmsg() and one that processes each individual packet, to reduce the level of indentation and make the functions a bit more readable. Then I'll merge it :) I guess in the future, we want to put a "cork" on the output until all packets from a single recvmmsg() have been received, so that we can do sendmmsg() on the resulting outgoing packets.> More is yet to come: I'll have a look at extending the tun/tap interface > to send/receive several packets at a time, and then also using sendmmsg > will again improve performance.Last time I checked, the send/recvmmsg() functionality is actually there in the kernel code, but just not exposed to userspace. It would be great to have access to it though. -- Met vriendelijke groet / with kind regards, Guus Sliepen <guus at tinc-vpn.org> -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: <http://www.tinc-vpn.org/pipermail/tinc-devel/attachments/20151202/bcacc364/attachment-0001.sig>
Guus Sliepen, on Wed 02 Dec 2015 13:53:37 +0100, wrote:> I guess in the future, we want to put a "cork" on the output until all > packets from a single recvmmsg() have been received, so that we can do > sendmmsg() on the resulting outgoing packets.Yes.> > More is yet to come: I'll have a look at extending the tun/tap interface > > to send/receive several packets at a time, and then also using sendmmsg > > will again improve performance. > > Last time I checked, the send/recvmmsg() functionality is actually there > in the kernel code, but just not exposed to userspace. It would be great > to have access to it though.Well, the tun driver currently expects the writer (for instance) to put one packet at a time. If given more than one packet it's not able to consume only one packet and let the rest for a further call. But it shouldn't be too hard to fix that. Samuel
Hello, Guus Sliepen, on Wed 02 Dec 2015 13:53:37 +0100, wrote:> That's great! It would be good though to split > handle_incoming_vpn_data() into a function that does the actual > recvfrom/mmsg() and one that processes each individual packet, to reduce > the level of indentation and make the functions a bit more readable. > Then I'll merge it :)Here it is. Samuel -------------- next part -------------- diff --git a/configure.ac b/configure.ac index 5cdd642..fcac9d2 100644 --- a/configure.ac +++ b/configure.ac @@ -187,7 +187,7 @@ AC_CHECK_TYPES([socklen_t, struct ether_header, struct arphdr, struct ether_arp, dnl Checks for library functions. AC_TYPE_SIGNAL -AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random select strdup strerror strsignal strtol system unsetenv usleep vsyslog writev], +AC_CHECK_FUNCS([asprintf daemon fchmod flock ftime fork get_current_dir_name gettimeofday mlockall pselect putenv random recvmmsg select strdup strerror strsignal strtol system unsetenv usleep vsyslog writev], [], [], [#include "src/have.h"] ) diff --git a/src/net_packet.c b/src/net_packet.c index e67857c..174db34 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -695,31 +695,26 @@ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) { return n; } -void handle_incoming_vpn_data(int sock) { - vpn_packet_t pkt; +#ifdef HAVE_RECVMMSG +#define MAX_MSG 256 +#else +#define MAX_MSG 1 +#endif + +static void handle_incoming_vpn_packet(int sock, vpn_packet_t *pkt, sockaddr_t *from) { char *hostname; - sockaddr_t from; - socklen_t fromlen = sizeof(from); node_t *n; - pkt.len = recvfrom(listen_socket[sock].udp, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen); - - if(pkt.len < 0) { - if(!sockwouldblock(sockerrno)) - logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno)); - return; - } - - sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */ + sockaddrunmap(from); /* Some braindead IPv6 implementations do stupid things. */ - n = lookup_node_udp(&from); + n = lookup_node_udp(from); if(!n) { - n = try_harder(&from, &pkt); + n = try_harder(from, pkt); if(n) - update_node_udp(n, &from); + update_node_udp(n, from); else ifdebug(PROTOCOL) { - hostname = sockaddr2hostname(&from); + hostname = sockaddr2hostname(from); logger(LOG_WARNING, "Received UDP packet from unknown source %s", hostname); free(hostname); return; @@ -730,5 +725,55 @@ void handle_incoming_vpn_data(int sock) { n->sock = sock; - receive_udppacket(n, &pkt); + receive_udppacket(n, pkt); +} + +void handle_incoming_vpn_data(int sock) { + vpn_packet_t pkt[MAX_MSG]; + sockaddr_t from[MAX_MSG]; +#ifdef HAVE_RECVMMSG + struct mmsghdr msg[MAX_MSG]; + struct iovec iov[MAX_MSG]; +#else + socklen_t fromlen = sizeof(from[0]); +#endif + int num = 1, i; + +#ifdef HAVE_RECVMMSG + for(i = 0; i < MAX_MSG; i++) + { + msg[i].msg_hdr.msg_name = &from[i].sa; + msg[i].msg_hdr.msg_namelen = sizeof(from[i]); + iov[i].iov_base = &pkt[i].seqno; + iov[i].iov_len = MAXSIZE; + msg[i].msg_hdr.msg_iov = &iov[i]; + msg[i].msg_hdr.msg_iovlen = 1; + msg[i].msg_hdr.msg_control = NULL; + msg[i].msg_hdr.msg_controllen = 0; + } + num = recvmmsg(listen_socket[sock].udp, msg, MAX_MSG, MSG_DONTWAIT, NULL); +#else + pkt[0].len = recvfrom(listen_socket[sock].udp, (char *) &pkt[0].seqno, MAXSIZE, 0, &from[0].sa, &fromlen); +#endif + +#ifdef HAVE_RECVMMSG + if(num < 0) +#else + if(pkt[0].len < 0) +#endif + { + if(!sockwouldblock(sockerrno)) + logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno)); + return; + } + + for(i = 0; i < num; i++) + { +#ifdef HAVE_RECVMMSG + pkt[i].len = msg[i].msg_len; + if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE) + continue; +#endif + handle_incoming_vpn_packet(sock, &pkt[i], &from[i]); + } }