sleinen / samplicator

Send copies of (UDP) datagrams to multiple receivers, with optional sampling and spoofing
GNU General Public License v2.0
393 stars 133 forks source link

only first 1500 bytes from large UDP datagrams is copied #34

Closed pangorgo closed 8 years ago

pangorgo commented 8 years ago

I am working with traffic from application which sends UDP datagrams longer than 1500 Bytes so they are fragmented in IP layer.

in samplicate.c PDU_SIZE is fixed to 1500 here #define PDU_SIZE 1500

result of which - maximum 1500 bytes from buffer is copied and no warning nor error is thrown in case of longer UDP datagram.

I see that further there is some test that should catch longer datagram:

if ((n = recvfrom (ctx->fsockfd, (char*)fpdu, sizeof (fpdu), 0, (struct sockaddr *) &remote_address, &addrlen)) == -1) {
  fprintf (stderr, "recvfrom(): %s\n", strerror(errno));
  exit (1);
}
if (n > PDU_SIZE) {
  fprintf (stderr, "Warning: %d excess bytes discarded\n", n-PDU_SIZE);
  n = PDU_SIZE;
}

But at least in my case (kernel 2.6.18) it fails to catch it. I think the reason is lack of MSG_TRUNC flag in recvfrom() call for modern kernels (since 2.2).

My workaround was changing PDU_SIZE to larger value, and it solved problem for my long UDP case but not sure if it won't broke something else - for example TCP handling.

sleinen commented 8 years ago

Thanks for the report! It is a bit ugly that the max. PDU size is hardwired; it would be great if we could make this configurable by a command-line option. (Also, 1500 bytes is a somewhat funny size at the UDP level.) Your workaround is fine. Don't worry about TCP—the samplicator really only works for UDP.

As you point out, the "excess bytes" test seems broken in the code. It would be great to fix that. Thanks for pointing to the MSG_TRUNC flag! We'd probably need to change from using recvfrom() to recvmsg() to see that flag. Correct? Personally I think that would be a good move anyway, because it opens the door to receiving several datagrams in one call (scatter/gather), which would be a performance win in situations with high packet rates.

Would you be able to propose a patch for correct truncation detection? I'll happily review it.

sleinen commented 8 years ago

I changed the bug title a bit, hope that's OK!

pangorgo commented 8 years ago

Sure, in couple of days should find some time to propose a fix.

msandstedt commented 4 years ago

In Linux, there is a really clean way to solve this problem, and in many cases you don't even need to malloc. It's a datagram. Just ask the kernel how much data is waiting for you.

void always_get_entire_pdu(int fd)
{
    ssize_t pending = recv(fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
    if (pending < 0) {
        return;
    }
    // Now we know how big the datagram is and can push that many bytes on the stack.
    #if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
    uint8_t buf[pending];
    #else
    uint8_t *buf = alloca(pending);
    #endif
    ssize_t received = recv(fd, buf, pending, 0); 
}
PolynomialDivision commented 2 years ago

@msandstedt That looks nice. why don't you open a PR? :)