Unable to receive known reply when broadcasting UDP datagrams over LAN using Sockets or UdpClient

1

I have searched for 2 days and found many, many questions/answers to what appears to be this same issue, with some differences, however none really seem to provide a solution.

I am implementing a library for controlling a DMX system (ColorKinetics devices) directly without an OEM controller. This involves communicating with an Ethernet-enabled power supply (PDS) connected to my home LAN, through a router, which drives the lighting fixtures. The PDS operates on a specific port (6038) and responds to properly formatted datagrams broadcast over the network.

I can successfully broadcast a simple DMX message (Header + DMX data), which gets picked up by the PDS and applied to connected lighting fixtures, so one-way communication is not an issue.

My issue is that I am now trying to implement a device discovery function to detect the PDS(s) and attached lights on the LAN, and I am not able to receive datagrams which are (absolutely) being sent back from the PDS. I can successfully transmit a datagram which instructs the devices to reply, and I can see the reply coming back in WireShark, but my application does not detect the reply.

I also tried running a simple listener app on another machine, which could detect the initial broadcast, but could not hear the return datagram either, however I figure this wouldn't work since the return packet is addressed to the original sender IP address.

I initially tried implementing via UdpClient, then via Sockets, and both produce the same result no matter what options and parameters I seem to specify.

Here is my current, very simple code to test functionality, currently using Sockets.

byte[] datagram = new CkPacket_DiscoverPDSRequestHeader().ToPacket();
Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);    
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("192.168.1.149"), 6039);

public Start()
{    
    // Start listener
    new Thread(() =>
    {
        Receive();
    }).Start();

    sender.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    sender.EnableBroadcast = true;
    // Bind the sender to known local IP and port 6039
    sender.Bind(ep);
}

public void Send()
{            
    // Broadcast the datagram to port 6038
    sender.SendTo(datagram, new IPEndPoint(IPAddress.Broadcast, 6038));
}

public void Receive()
{
    Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    receiver.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    receiver.EnableBroadcast = true;

    // Bind the receiver to known local IP and port 6039 (same as sender)
    IPEndPoint EndPt = new IPEndPoint(IPAddress.Parse("192.168.1.149"),6039);
    receiver.Bind(EndPt);

    // Listen
    while (true)
    {
        byte[] receivedData = new byte[256];

        // Get the data
        int rec = receiver.Receive(receivedData);

        // Write to console the number of bytes received
        Console.WriteLine($"Received {rec} bytes");
    }    
}

The sender and receiver are bound to an IPEndPoint with the local IP and port 6039. I did this because I could see that each time I initialized a new UdpClient, the system would dynamically assign an outgoing port, which the PDS would send data back to. Doing it this way, I can say that the listener is definitely listening on the port which should receive the PDS response (6039). I believe that since I have the option ReuseAddress set to true, this shouldn't be a problem (no exceptions thrown).

Start() creates a new thread to contain the listener, and initializes options on the sending client.

Send() successfully broadcasts the 16-byte datagram which is received by the PDS on port 6038, and generates a reply to port 6039 (Seen in WireShark)

Receive() does not receive the datagram. If I bind the listener to port 6038, it will receive the original 16-byte datagram broadcast.

Here is the WireShark data:

Wireshark

I have looked at using a library like SharpPCap, as many answers have suggested, but there appear to be some compatibility issues in the latest release that I am not smart enough to circumvent, which prevent the basic examples from functioning properly on my system. It also seems like this sort of basic functionality shouldn't require that type of external dependency. I've also seen many other questions/answers where the issue was similar, but it was solved by setting this-or-that parameter for the Socket or UdpClient, of which I have tried every combination to no avail.

I have also enabled access permissions through windows firewall, allowed port usage, and even completely disabled the firewall, to no success. I don't believe the issue would be with my router, since messages are getting to Wireshark.

UPDATE 1

Per suggestions, I believe I put the listener Socket in promiscuous mode as follows:

    Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
    receiver.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);
    receiver.EnableBroadcast = true;

    IPEndPoint EndPt = new IPEndPoint(IPAddress.Parse("192.168.1.149"), 0);

    receiver.Bind(EndPt);
    receiver.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, null);

This resulted in the listener receiving all sorts of network traffic, including the outbound requests, but still no incoming reply.

UPDATE 2

As Viet suggested, there is some sort of addressing problem in the request datagram, which is formatted as such:

    public class CkPacket_DiscoverPDSRequest : BytePacket
    {
        public uint magic = 0x0401dc4a;
        public ushort version = 0x0100;
        public ushort type = 0x0100;
        public uint sequence = 0x00000000;
        public uint command = 0xffffffff;
    }

If I change the command field to my broadcast address 192.168.1.149' or192.168.255.255`, my listener begins detecting the return packets. I admittedly do not know what this field is supposed to represent, and my original guess was to just put in a broadcast address since the point of the datagram is to discover all devices on the network. This is obviously not the case, though I am still not sure the exact point of it.

Either way, thank you for the help, this is progress.

c#
sockets
udp
udpclient
asked on Stack Overflow Nov 14, 2019 by kenaschmidt • edited Nov 15, 2019 by kenaschmidt

2 Answers

0

So in actuality it turns out that my issue was with the formatting of the outgoing datagram. The command field needs to be an address on the local subnet 192.168.xxx.xxx, and not 255.255.255.255... for whatever reason this was causing the packet to be filtered somewhere before getting to my application, though WireShark could still see it. This may be common sense in this type of work but being relatively ignorant as to network programming as well as the specifics of this interface it wasn't something I had considered.

Making the change allows a simple UdpClient send/receive to function perfectly.

Much thanks to Viet Hoang for helping me find this!

answered on Stack Overflow Nov 16, 2019 by kenaschmidt
-1

As you've already noted, you don't need to bind to send out a broadcast but it uses a random source port.

If you adjust your code to not bind the sender, your listener should behave as expected again:

Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);    
sender.EnableBroadcast = true;
Thread read_thread;

public Start()
{    
    // Start listener
    read_thread = new Thread(Receive);
    read_thread.Start();
}

The issue you've bumped into is that the operating system kernel is only delivering packets up to one socket binder (first come first serve basis).

If you want true parallel read access, you'll need to look into sniffing example such as: https://stackoverflow.com/a/12437794/8408335.

Since you are only looking to source the broadcast from the same ip/port, you simply need to let the receiver bind first. If you add in a short sleep after kicking off the receive thread, and before binding the sender, you will be able to see the expected results.

public Start()
{    
    // Start listener
    new Thread(() =>
    {
        Receive();
    }).Start();

    Thread.Sleep(100);

    sender.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    sender.EnableBroadcast = true;
    // Bind the sender to known local IP and port 6039
    sender.Bind(ep);
}

Extra note: You can quickly test your udp sockets from a linux box using netcat:

# echo "hello" | nc -q -1 -u 192.168.1.149 6039 -

- Edit -

Problem Part #2

The source address of "255.255.255.255" is invalid. Did a quick test with two identical packets altering the source ip:

https://i.stack.imgur.com/BvWIa.jpg

Only the one with the valid source IP was printed to the console.

Received 26 bytes
Received 26 bytes
answered on Stack Overflow Nov 15, 2019 by vhoang • edited Nov 15, 2019 by vhoang

User contributions licensed under CC BY-SA 3.0