LuaSocket and multiple UDP peers

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

LuaSocket and multiple UDP peers

Rena
If I'm understanding the documentation of recvfrom() correctly, a UDP listening socket can receive packets from any peer, and recvfrom() will return the packet plus the address and port it was received from. That suggests to me that it's possible to receive a long message from peer A, which arrives in two separate packets, and in between them arrives a packet from peer B.

I've been writing a socket library myself and want to support the same functionality as LuaSocket, in which receive() accepts patterns *l and *a. However I'm not sure what to do about this possibility, nor what LuaSocket does if you specify *a and receive messages from multiple peers. Does it just concat them all together disregarding the source information? Or am I misunderstanding how UDP sockets work?

--
Sent from my Game Boy.
Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

William Ahern
On Tue, Apr 23, 2013 at 07:23:32PM -0400, Rena wrote:
> If I'm understanding the documentation of recvfrom() correctly, a UDP
> listening socket can receive packets from any peer


Yes, presuming you haven't created an association with connect(2) (in which
case the kernel will filter them), nor bound it to an address which can only
accept packets from a limited set of networks (e.g. 127.0.0.1).

Note that "listening socket" is ambiguous. listen(2) is invalid on UDP
sockets, because they're not connection-oriented, and unlike connect(2)
there's no alternative behavior specified. I assume by "listening" that you
mean simply bound and reading packets.

> and recvfrom() will return the packet plus the address and port it was
> received from. That suggests to me that it's possible to receive a long
> message from peer A, which arrives in two separate packets, and in between
> them arrives a packet from peer B.
>
> I've been writing a socket library myself and want to support the same
> functionality as LuaSocket, in which receive() accepts patterns *l and *a.
> However I'm not sure what to do about this possibility, nor what LuaSocket
> does if you specify *a and receive messages from multiple peers. Does it
> just concat them all together disregarding the source information? Or am I
> misunderstanding how UDP sockets work?

UDP preserves message boundaries, so UDP messages will never be
concatenated. They may be truncated, however--especially if they're very
long*--or lost entirely. Lost** UDP messages are common on high-load
servers--e.g. a DNS server or client sending or receiving many packets per
second.


* They can also be fragmented, but this is transparent to the application.

** Corrupt message are also more common, and on Linux a TOUTTOC bug can be
exposed in poorly written software because select() will poll ready but the
read will fail--Linux doesn't verify the checksum until copying the packet
into userspace. This makes processes hang which assumed it was unnecessary
to mark UDP sockets non-blocking when you didn't want blocking semantics.


Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

William Ahern
On Tue, Apr 23, 2013 at 04:53:01PM -0700, William Ahern wrote:
> On Tue, Apr 23, 2013 at 07:23:32PM -0400, Rena wrote:
<snip>

> > I've been writing a socket library myself and want to support the same
> > functionality as LuaSocket, in which receive() accepts patterns *l and *a.
> > However I'm not sure what to do about this possibility, nor what LuaSocket
> > does if you specify *a and receive messages from multiple peers. Does it
> > just concat them all together disregarding the source information? Or am I
> > misunderstanding how UDP sockets work?
>
> UDP preserves message boundaries, so UDP messages will never be
> concatenated. They may be truncated, however--especially if they're very
> long*--or lost entirely. Lost** UDP messages are common on high-load
> servers--e.g. a DNS server or client sending or receiving many packets per
> second.

Just to be clear:

1) By never concatenated I mean recv() or recvfrom() will only every return
a single message, which generally means a message sent by a single call to
send() or sendto()**.

2) By truncated I mean that if the message was 1025 bytes, but you called
recvfrom() with a buffer limit of 1024 bytes, 1 byte of the message will be
lost entirely--the UDP stack will discard the remainder of a message not
copied to the application.


** On Unix read() and write() also work the same way as recv() and send().


Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

Rena
In reply to this post by William Ahern
On Tue, Apr 23, 2013 at 7:53 PM, William Ahern <[hidden email]> wrote:
On Tue, Apr 23, 2013 at 07:23:32PM -0400, Rena wrote:
> If I'm understanding the documentation of recvfrom() correctly, a UDP
> listening socket can receive packets from any peer


Yes, presuming you haven't created an association with connect(2) (in which
case the kernel will filter them), nor bound it to an address which can only
accept packets from a limited set of networks (e.g. 127.0.0.1).

Note that "listening socket" is ambiguous. listen(2) is invalid on UDP
sockets, because they're not connection-oriented, and unlike connect(2)
there's no alternative behavior specified. I assume by "listening" that you
mean simply bound and reading packets.

> and recvfrom() will return the packet plus the address and port it was
> received from. That suggests to me that it's possible to receive a long
> message from peer A, which arrives in two separate packets, and in between
> them arrives a packet from peer B.
>
> I've been writing a socket library myself and want to support the same
> functionality as LuaSocket, in which receive() accepts patterns *l and *a.
> However I'm not sure what to do about this possibility, nor what LuaSocket
> does if you specify *a and receive messages from multiple peers. Does it
> just concat them all together disregarding the source information? Or am I
> misunderstanding how UDP sockets work?

UDP preserves message boundaries, so UDP messages will never be
concatenated. They may be truncated, however--especially if they're very
long*--or lost entirely. Lost** UDP messages are common on high-load
servers--e.g. a DNS server or client sending or receiving many packets per
second.


* They can also be fragmented, but this is transparent to the application.

** Corrupt message are also more common, and on Linux a TOUTTOC bug can be
exposed in poorly written software because select() will poll ready but the
read will fail--Linux doesn't verify the checksum until copying the packet
into userspace. This makes processes hang which assumed it was unnecessary
to mark UDP sockets non-blocking when you didn't want blocking semantics.



Yes, by listening I meant able to receive packets, sorry for the confusion. Am I correct in understanding that a single message sent by a peer will always arrive as a single message at the listening application?

Actually, that still doesn't make it perfectly clear how *a should work with UDP, since the peer itself might split its message into several pieces. It seems like blindly reading until the socket is closed in this situation would be a great way to let other peers interfere with the transmission...

--
Sent from my Game Boy.
Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

William Ahern
On Tue, Apr 23, 2013 at 08:15:24PM -0400, Rena wrote:
> On Tue, Apr 23, 2013 at 7:53 PM, William Ahern
> <[hidden email]>wrote:
<snip>
> Yes, by listening I meant able to receive packets, sorry for the confusion.
> Am I correct in understanding that a single message sent by a peer will
> always arrive as a single message at the listening application?

Yes. UDP preserves message boundaries, and it will always arrive (if it
arrives, and if it's not dropped by the UDP stack) as a single message, and
will be dequeued in a single call to recv, recvfrom, or read.

> Actually, that still doesn't make it perfectly clear how *a should work
> with UDP, since the peer itself might split its message into several
> pieces.

If the _application_ peer splits its logical message into several pieces,
then of course each individual piece will be received atomically by the
recipient, possibly interleaved with other pieces by other senders. Your
application "pieces" are messages as far as UDP is concerned.

A "message" in UDP is functionally equivalent to a single send or recv call,
not whatever your application defines it as. If the application requires
logical messaging which span multiple send calls, then it needs to implement
an additional layer of framing at both end points.

I'd prefer to abstain from using ambiguous OSI layering terminology, but you
can think of UDP as layer 4, and your logical framing as something higher
than layer 4. If your logical framing isn't satisfied by UDP itself, then
you need to implement it separately.

> It seems like blindly reading until the socket is closed in this situation
> would be a great way to let other peers interfere with the transmission...

If you mean flooding, then yes it could be a problem, but usually only if
done deliberately, in which case you may have other problems. But barring
address spoofing and bandwidth issues, it doesn't by itself cause problems
unless the application makes unwarranted assumptions.

You can always use connect(2) to create an association between two UDP end
points. The kernel will then drop messages for you, so you don't have to
check the IP address.


Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

Rena
On Tue, Apr 23, 2013 at 9:07 PM, William Ahern <[hidden email]> wrote:
On Tue, Apr 23, 2013 at 08:15:24PM -0400, Rena wrote:
> On Tue, Apr 23, 2013 at 7:53 PM, William Ahern
> <[hidden email]>wrote:
<snip>
> Yes, by listening I meant able to receive packets, sorry for the confusion.
> Am I correct in understanding that a single message sent by a peer will
> always arrive as a single message at the listening application?

Yes. UDP preserves message boundaries, and it will always arrive (if it
arrives, and if it's not dropped by the UDP stack) as a single message, and
will be dequeued in a single call to recv, recvfrom, or read.

> Actually, that still doesn't make it perfectly clear how *a should work
> with UDP, since the peer itself might split its message into several
> pieces.

If the _application_ peer splits its logical message into several pieces,
then of course each individual piece will be received atomically by the
recipient, possibly interleaved with other pieces by other senders. Your
application "pieces" are messages as far as UDP is concerned.

A "message" in UDP is functionally equivalent to a single send or recv call,
not whatever your application defines it as. If the application requires
logical messaging which span multiple send calls, then it needs to implement
an additional layer of framing at both end points.

I'd prefer to abstain from using ambiguous OSI layering terminology, but you
can think of UDP as layer 4, and your logical framing as something higher
than layer 4. If your logical framing isn't satisfied by UDP itself, then
you need to implement it separately.

> It seems like blindly reading until the socket is closed in this situation
> would be a great way to let other peers interfere with the transmission...

If you mean flooding, then yes it could be a problem, but usually only if
done deliberately, in which case you may have other problems. But barring
address spoofing and bandwidth issues, it doesn't by itself cause problems
unless the application makes unwarranted assumptions.

You can always use connect(2) to create an association between two UDP end
points. The kernel will then drop messages for you, so you don't have to
check the IP address.



Well flooding would be one way, but I was thinking more of a communication between two systems using UDP, and a malicious third party just starts sending some of its own packets to one of the clients on that same port; if you just read everything until the socket is closed, disregarding who it came from, and return it all as one string, the third party could inject some malicious packets into the stream. But you're right, that would be a silly way to do things... of course, writing a general-purpose socket library and not a particular application, you have to be ready for the possibility that someone wants to use your library to communicate with a client that does silly things like that.

I wasn't aware you could use connect() on a UDP socket. So, would it be done like this?

void processMessage(char *buffer, ssize_t size) { ... }

int done = 0;
int sockfd = socket(SOCK_DGRAM, AF_INET, 0);
bind(sockfd, AF_INET, someAddress, somePort);

char buffer[8192];
struct sockaddr_in peer;
socklen_t peer_len = sizeof(peer);
ssize_t recvsz = recvfrom(sockfd, buffer, sizeof(buffer), 0,
    (struct sockaddr*)&peer, &peer_len);

processMessage(buffer, recvsz);
connect(sockfd, peer.sin_addr, peer.sin_port);

while(!done) {
    recvsz = recv(sockfd, buffer, sizeof(buffer), 0);
    processMessage(buffer, recvsz);
}

or am I still missing something?

--
Sent from my Game Boy.
Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

Sean Conner
It was thus said that the Great Rena once stated:

> On Tue, Apr 23, 2013 at 9:07 PM, William Ahern
> <[hidden email]>wrote:
>
> > On Tue, Apr 23, 2013 at 08:15:24PM -0400, Rena wrote:
> > > On Tue, Apr 23, 2013 at 7:53 PM, William Ahern
> > > <[hidden email]>wrote:
> > <snip>
> > > Yes, by listening I meant able to receive packets, sorry for the
> > confusion.
> > > Am I correct in understanding that a single message sent by a peer will
> > > always arrive as a single message at the listening application?
> >
> > Yes. UDP preserves message boundaries, and it will always arrive (if it
> > arrives, and if it's not dropped by the UDP stack) as a single message, and
> > will be dequeued in a single call to recv, recvfrom, or read.
> >
> > > Actually, that still doesn't make it perfectly clear how *a should work
> > > with UDP, since the peer itself might split its message into several
> > > pieces.
> >
> > If the _application_ peer splits its logical message into several pieces,
> > then of course each individual piece will be received atomically by the
> > recipient, possibly interleaved with other pieces by other senders. Your
> > application "pieces" are messages as far as UDP is concerned.
> >
> > A "message" in UDP is functionally equivalent to a single send or recv
> > call,
> > not whatever your application defines it as. If the application requires
> > logical messaging which span multiple send calls, then it needs to
> > implement
> > an additional layer of framing at both end points.
> >
> > I'd prefer to abstain from using ambiguous OSI layering terminology, but
> > you
> > can think of UDP as layer 4, and your logical framing as something higher
> > than layer 4. If your logical framing isn't satisfied by UDP itself, then
> > you need to implement it separately.
> >
> > > It seems like blindly reading until the socket is closed in this
> > situation
> > > would be a great way to let other peers interfere with the
> > transmission...
> >
> > If you mean flooding, then yes it could be a problem, but usually only if
> > done deliberately, in which case you may have other problems. But barring
> > address spoofing and bandwidth issues, it doesn't by itself cause problems
> > unless the application makes unwarranted assumptions.
> >
> > You can always use connect(2) to create an association between two UDP end
> > points. The kernel will then drop messages for you, so you don't have to
> > check the IP address.
> >
> >
> >
> Well flooding would be one way, but I was thinking more of a communication
> between two systems using UDP, and a malicious third party just starts
> sending some of its own packets to one of the clients on that same port; if
> you just read everything until the socket is closed, disregarding who it
> came from, and return it all as one string, the third party could inject
> some malicious packets into the stream. But you're right, that would be a
> silly way to do things... of course, writing a general-purpose socket
> library and not a particular application, you have to be ready for the
> possibility that someone wants to use your library to communicate with a
> client that does silly things like that.
>
> I wasn't aware you could use connect() on a UDP socket. So, would it be
> done like this?
>
> void processMessage(char *buffer, ssize_t size) { ... }
>
> int done = 0;
> int sockfd = socket(SOCK_DGRAM, AF_INET, 0);
> bind(sockfd, AF_INET, someAddress, somePort);
>
> char buffer[8192];
> struct sockaddr_in peer;
> socklen_t peer_len = sizeof(peer);
> ssize_t recvsz = recvfrom(sockfd, buffer, sizeof(buffer), 0,
>     (struct sockaddr*)&peer, &peer_len);
>
> processMessage(buffer, recvsz);
> connect(sockfd, peer.sin_addr, peer.sin_port);
>
> while(!done) {
>     recvsz = recv(sockfd, buffer, sizeof(buffer), 0);
>     processMessage(buffer, recvsz);
> }
>
> or am I still missing something?

  I feel you are missing something, and for this explanation, I'll be using
my own net library [1] as it's Lua and we're on a Lua list here.

  On the client side:

        server = net.address('fc00::dead:beef',15151,'udp')
        sock   = net.socket(server.family,'udp')

  You can now sent "messages" to the server:

        sock:write(server,"this is a message")

  If you split it up, like:

        sock:write(server,"this is ")
        sock:write(server,"a message")

  The server process will receive two separate UDP packets.  Now, if you
send a very large packet:

        data = some_big_string_that_is_48_k_in_size
        sock:write(server,data)

  The IP stack on the client side will most likely break it up into multiple
packets---this is IP fragmentation.  The IP stack on the receiving end will
collect all the fragments that comprise the packet (and here I'm being very
loose with the terminology) and when all 48k worth have been reassembled,
only then will the IP stack send the packet to the receiving process.

  So in this case:

        data = some_big_string_that_is_48_k_in_size
        sock:write(server,data)
        data = more_big_data_that_is_now_60_k_in_size
        sock:write(server,data)

  the server process will still receive only two UDP packets, even though
across an Ethernet based network, there will probably be around 100 packets
sent.  

  Now, on the server side of things:

        addr = net.address('fc00::dead:beef',15151,'udp')
        sock = net.socket(addr.family,'udp')
        sock.reuseaddr = true
        sock:bind(addr)

        function mainloop()
          local remaddr,data,err = sock:read()
          if data == nil then
            report_error(err)
            return mainloop()
          end

          -- rest of code
          return mainloop()
        end

(I use tail calls so I can "continue" the loop)

  sock:read() is implemented via recvfrom(), so I have the address of the
sender so I can send replies back to:

        if data == 'time?' then
          sock:send(remaddr,os.date())
        end

  I can also use the remaddr as a key to maintain data per client---so, for
example:

        local remaddr,data = sock:read() -- assume okay for this example

        -- checks for lost and duplicated packets left to the read

        if client_list[remaddr] == nil then
          client_list[remaddr] = data
        else
          client_list[remaddr] = client_list[remaddr] .. data
        end

  Now, about connect() and UDP sockets.  The use of connect() on a datagram
based socket (UDP) means the client program can use the read() and write()
system calls on the socket, since the kernel "knows" which address to send
the packets to (think of it as binding the remote address of a socket).  If
you don't use connect(), then you *have* to use sendto() to send the packet.
A server program should not use connect(), as it probably has to talk to
multiple clients at a time.

  Another thing to remember about UDP---it's not connection oriented, so the
client side can close its socket without the server ever being the wiser.
It's up to higher levels of code to detect a non-communicating end (server
or client).

  -spc (And yes, those are IPv6 addresses)

[1] https://github.com/spc476/lua-conmanorg/blob/master/src/net.c


Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

David Favro
In reply to this post by Rena
On 04/24/2013 07:15 AM, Rena wrote:
> Actually, that still doesn't make it perfectly clear how *a should
> work with UDP, since the peer itself might split its message into
> several pieces. It seems like blindly reading until the socket is
> closed in this situation would be a great way to let other peers
> interfere with the transmission...

In this situation one should seriously consider TCP, which was created
for exactly this purpose.  If the application is splitting the logical
message into several physical messages for transmission as UDP, in
addition to the packet-injection problem you've mentioned, the packets
may arrive out of order, or only some of them may arrive at all.  The
issues of acknowledgement and retransmission are hard enough without
packet-reordering too, and TCP takes care of all of that, typically in
the kernel.

TCP is also vulnerable to packet-injection attacks, but less so than
UDP; but any protocol that attempts to truly protect the integrity of
messages needs to use a solid cryptographic message authentication
protocol which is typically (e.g. TLS) separate from the underlying
transport protocol, although there is an attempt to get momentum behind
a combined transport and security protocol [1].

Since you're writing a general-purpose socket library, you can probably
just ignore these issues and leave them to the application programmer
above you or the system programmer below you.

So, what I'm saying is that it isn't very important what meaning you
assign to "*a" because it would rarely if ever be useful anyhow.  UDP
can be useful for some multiple-packet applications (e.g. streaming
media) but I don't think that "*a" is going to make sense in those cases
either.

-- David

[1]: http://tcpcrypt.org/


Reply | Threaded
Open this post in threaded view
|

Re: LuaSocket and multiple UDP peers

Jorge Visca
On 04/24/2013 04:01 AM, David Favro wrote:
> On 04/24/2013 07:15 AM, Rena wrote:
> So, what I'm saying is that it isn't very important what meaning you
> assign to "*a" because it would rarely if ever be useful anyhow.  UDP
> can be useful for some multiple-packet applications (e.g. streaming
> media) but I don't think that "*a" is going to make sense in those cases
> either.

I think this sums it up pretty well: if the user want something like
"*a", the sane method is using TCP. "*l" and "number" can be served by
UDP and TCP, depending on reliability and ordering requirements.

Jorge