Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG]udp stream packet size too large for end-to-end route #400

Open
alecmyers opened this issue Jul 30, 2023 · 6 comments
Open

[BUG]udp stream packet size too large for end-to-end route #400

alecmyers opened this issue Jul 30, 2023 · 6 comments

Comments

@alecmyers
Copy link

alecmyers commented Jul 30, 2023

  • RTLSDR-Airband version 4.1.1
  • make options used to build the program: -
  • Hardware platform Pi v4
  • Operating system name and version: Debian 12

symptom: UDP stream not received at remote host.
TCP dump shows:

10:21:56.442031 IP radiostream.59703 > xx.xx.xx.xx.XX: UDP, length 4000
10:21:56.596971 IP radiostream.59703 > xx.xx.xx.xx.XX: UDP, length 4000

MTU for ethernet interface is (of course) 1500, therefore externally routed UDP packets are (may be) silently discarded instead of fragmented.

MTU for loopback interface is 65536 - so (guessing) the packet size of 4000 is set internally? Data is received on loopback interface.

Can I suggest a new option for udp_stream of "packet_size"?

config follows:

devices:
({
type = "rtlsdr";
index = 0;
gain = 45;
centerfreq = 118.15;
correction = 0;
sample_rate = 1.0;
channels: (
{
freq = 118.2;
lowpass = 5000;
highpass = 200;
squelch_threshold = -44;
outputs: (
{
type = "icecast";
server = "127.0.0.1";
port = 8000;
mountpoint = "xxx.mp3";
name = "xxx";
genre = "ATC";
username = "xxx";
password = "xxx";
},
{
type="file";
directory="/opt/audio";
filename_template="xxx";
continuous=false;
include_freq=false;
append=true;
},
{
type = "udp_stream";
dest_address = "xxx.xxx.com";
dest_port = 8123;
continuous = true;
},
{
type = "udp_stream";
dest_address = "127.0.0.1";
dest_port = 9001;
continuous = true;
}
);
}
);
}
);

@alecmyers
Copy link
Author

Following further research, I think the title of this issue isn't accurate - the kernel should (and perhaps does) fragment IP packets to match the mtu, but there's no path mtu negotiation available here end-to-end and hosts are permitted silently to drop udp datagrams longer than 576 bytes.

As a nasty hack I changed rtl_airband.h:
#define WAVE_BATCH WAVE_RATE / 32 //was 8

outgoing UDP packets are now 1000 bytes long and reach their destination correctly.

So although it's not a defect in rtl_airband per-se, a configurable udp packet size would still be helpful.

@alecmyers alecmyers changed the title [BUG]udp stream fails to respect interface MTU [BUG]udp stream packet size too large for end-to-end route Jul 30, 2023
@charlie-foxtrot
Copy link
Owner

This is odd, I would expect the network stack to segment as necessary . . .

Is there something special / unique about your setup, or would you anticipate everyone using udp off of localhost to run into this?

I'm reluctant to put in a configuration for MTU, but, if this is something that everyone not using localhost is going to see, perhaps some different logic based on the destination.

Could you try modifying udp_stream_write() to call sendto() multiple times, each with chunks of 500 bytes (because that evenly divides the write) and see if that resolves your issue? If so then maybe something like "if destination isn't localhost then chop outputting writes into 500 byte chunks"

@alecmyers
Copy link
Author

I don't think there's anything particularly unusual about my setup: the source is running on a commercial broadband link, the destination is a cloud virtual server; there are various firewalls in between, but only what is usual for such a set up.

I did a manual MTU discovery with this at the destination:
nc -ulp 8123

and this at the source:
printf 'x%.0s' {1..1412} > /dev/udp/xxx.xxx.com/8123

The largest packet that arrives at the destination is a payload of 1412 (as shown) - increase to 1413 and the packet is dropped.

would you anticipate everyone using udp off of localhost to run into this?

With a source and destination on the same subnet this would be down to the physical layer MTU and fragmentation policy of the sending host, so likely not a problem.
For streaming UDP packets over a routed connection and particularly for via the Internet, where various tunnelling and VPN policies could be in place, without an MTU discovery process I think it would be wiser to stick to short UDP packets.

Could you try modifying udp_stream_write() to call sendto()....

Will do.

@donwade
Copy link

donwade commented Aug 6, 2023 via email

@alecmyers
Copy link
Author

alecmyers commented Aug 6, 2023

In theory, there's nothing wrong with a 4k UDP payload. Per RFC791, any IP datagram (including UDP) can be up to 64k long. Unless the 'do not fragment' bit is set, intermediate hosts can fragment packets, but they are not required to. RFC791 also says "It is recommended that hosts only send datagrams larger than 576 octets if they have assurance that the destination is prepared to accept the larger datagrams."

I believe under IPv6, only the source is permitted to fragment datagrams. In which case an intermediate hop with an MTU less than that of the source network will cause the data to be silently discarded, therefore only a datagram of 576 bytes or fewer is safe.

@83Lima
Copy link

83Lima commented Nov 29, 2023

Newbie here. I second the request for a packet size option. I am trying to use VLC to listen to the UDP stream of my local airport before becoming a source to LiveATC and RadarBox. My source, a Raspberry Pi4 model B, is fragmenting the packets to its outgoing MTU of 1500, the receiving client listening to the stream (MacOS on the same LAN) reassembles the packets to 4000 bytes (mono) or 8000 bytes (stereo), then passes the payload up to VLC. There, the data is dropped because VLC 3.0 and later only supports up to 1316 bytes.

I tried implementing the suggestion of chunking the data inside of udp_stream_write() by calling sendto() repeatedly, but I'm getting choppy audio and only seeing about 2000 bytes out of an 8000 byte payload. The generated packets do have a payload of 500 bytes as expected. I also tried 1000 bytes but with the same result.

Here's what I did, but keep in mind I am NOT a programmer, let alone know much about C++

void udp_stream_write(udp_stream_data *sdata, const float *data, size_t len) {
        if (sdata->send_socket != -1) {
                for (size_t i = 0; i < len/500; ++i) {
                        // Send without blocking or checking for success
                        sendto(sdata->send_socket, data + i*500, 500, MSG_DONTWAIT | MSG_NOSIGNAL, &sdata->dest_sockaddr, sdata->dest_sockaddr_len);
                }
        }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants