3

I have a Python app that broadcasts SSDP discovery requests. I noticed the devices I'm attempting to discover aren't always responding. Using Wireshark I found that only some of my broadcasts are reaching the wire. After some troubleshooting I isolated the source of the problem to the SSDP Discovery service - if I disable that service then my packet loss goes away. Also, if I use a multicast address other than SSDP (239.255.255.250) the problem also goes away. So it definitely seems SSDP is throttling my outgoing UDP broadcasts. Any idea why this is? Perhaps trying to coalesce broadcasts/limit traffic? I'm using Windows 7. The problem doesn't occur under OSX.

Here is a quick test app demonstrating the packet loss - both instances running on the same system, the sender instance transmits a packet every second and the receive reports any gaps in the test-defined packet number.

def testSend():
    seqNumber = 0
    while True:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
        sock.sendto(str(seqNumber), ("239.255.255.250", 1900))
        sock.close()
        print("Sent Seq #{:4d}  [{}]".format(seqNumber, time.ctime()))
        seqNumber += 1
        time.sleep(1)

def testReceive():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("", 1900))   
    mreq = struct.pack("4sl", socket.inet_aton("239.255.255.250"), socket.INADDR_ANY)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
    expectedSequenceNumber = 0
    while True:
        response = sock.recv(100)
        actualSequenceNumber = int(response)
        if actualSequenceNumber == expectedSequenceNumber:
            print("Good: Received Seq #{:>4s}  [{}]".format(response, time.ctime()))
        else:
            print("Bad: Expected Seq #{}, Got #{} ({} frames dropped) [{}]".format(expectedSequenceNumber, actualSequenceNumber, actualSequenceNumber - expectedSequenceNumber, time.ctime()))
        expectedSequenceNumber = actualSequenceNumber + 1

if sys.argv[1] == 'send':
    testSend()
elif sys.argv[1] == 'receive':
    testReceive()

Output on receiving side:

Good: Received Seq #   0  [Sun Sep 20 11:01:18 2015]
Good: Received Seq #   1  [Sun Sep 20 11:01:19 2015]
Good: Received Seq #   2  [Sun Sep 20 11:01:20 2015]
Good: Received Seq #   3  [Sun Sep 20 11:01:21 2015]
Good: Received Seq #   4  [Sun Sep 20 11:01:22 2015]
Bad: Expected Seq #5, Got #12 (7 frames dropped) [Sun Sep 20 11:01:30 2015]
Good: Received Seq #  13  [Sun Sep 20 11:01:31 2015]
Good: Received Seq #  14  [Sun Sep 20 11:01:32 2015]
Good: Received Seq #  15  [Sun Sep 20 11:01:33 2015]
Good: Received Seq #  16  [Sun Sep 20 11:01:34 2015]
Good: Received Seq #  17  [Sun Sep 20 11:01:35 2015]
Good: Received Seq #  18  [Sun Sep 20 11:01:36 2015]
Good: Received Seq #  19  [Sun Sep 20 11:01:37 2015]
Good: Received Seq #  20  [Sun Sep 20 11:01:38 2015]
Bad: Expected Seq #21, Got #51 (30 frames dropped) [Sun Sep 20 11:02:09 2015]
Good: Received Seq #  52  [Sun Sep 20 11:02:10 2015]
Good: Received Seq #  53  [Sun Sep 20 11:02:11 2015]
Good: Received Seq #  54  [Sun Sep 20 11:02:12 2015]
Good: Received Seq #  55  [Sun Sep 20 11:02:14 2015]
Good: Received Seq #  56  [Sun Sep 20 11:02:15 2015]
Good: Received Seq #  57  [Sun Sep 20 11:02:16 2015]
Good: Received Seq #  58  [Sun Sep 20 11:02:17 2015]
Good: Received Seq #  59  [Sun Sep 20 11:02:18 2015]
Good: Received Seq #  60  [Sun Sep 20 11:02:19 2015]
Bad: Expected Seq #61, Got #71 (10 frames dropped) [Sun Sep 20 11:02:30 2015]
Good: Received Seq #  72  [Sun Sep 20 11:02:31 2015]
Good: Received Seq #  73  [Sun Sep 20 11:02:32 2015]
Good: Received Seq #  74  [Sun Sep 20 11:02:33 2015]
Good: Received Seq #  75  [Sun Sep 20 11:02:34 2015]
Good: Received Seq #  76  [Sun Sep 20 11:02:35 2015]
Good: Received Seq #  77  [Sun Sep 20 11:02:36 2015]
Good: Received Seq #  78  [Sun Sep 20 11:02:37 2015]
Good: Received Seq #  79  [Sun Sep 20 11:02:38 2015]
Good: Received Seq #  80  [Sun Sep 20 11:02:39 2015]
Bad: Expected Seq #81, Got #110 (29 frames dropped) [Sun Sep 20 11:03:09 2015]
Good: Received Seq # 111  [Sun Sep 20 11:03:10 2015]
Good: Received Seq # 112  [Sun Sep 20 11:03:11 2015]
Good: Received Seq # 113  [Sun Sep 20 11:03:12 2015]
Good: Received Seq # 114  [Sun Sep 20 11:03:13 2015]
Good: Received Seq # 115  [Sun Sep 20 11:03:14 2015]
Good: Received Seq # 116  [Sun Sep 20 11:03:15 2015]
Good: Received Seq # 117  [Sun Sep 20 11:03:16 2015]
Good: Received Seq # 118  [Sun Sep 20 11:03:17 2015]
Good: Received Seq # 119  [Sun Sep 20 11:03:18 2015]
Bad: Expected Seq #120, Got #130 (10 frames dropped) [Sun Sep 20 11:03:30 2015]
Good: Received Seq # 131  [Sun Sep 20 11:03:31 2015]
Good: Received Seq # 132  [Sun Sep 20 11:03:32 2015]
Good: Received Seq # 133  [Sun Sep 20 11:03:33 2015]
Good: Received Seq # 134  [Sun Sep 20 11:03:34 2015]
Good: Received Seq # 135  [Sun Sep 20 11:03:35 2015]
Good: Received Seq # 136  [Sun Sep 20 11:03:36 2015]

Edit (10/06/15): I believe I've root-caused the issue. The Windows SSDP Discovery service appears periodically cycle which interface multicast packets go out on and also which interfaces incoming packets are, even on systems with only one physical network interface configured/online. On my system I have a single wired ethernet network and two virtual VMware network adapters (I'm not running in a VM - these are on the host side and are enabled but not being used). I modified the source ofthe utility above to support configuring which interface my broadcasts go out on via setsockopt(IP_MULTICAST_IF) and also which interface I listen to broadcasts on via setsockopt(IP_ADD_MEMBERSHIP). I then ran four instances of the utility - one sending on INADDR_ANY, one receiving on INADDR_ANY, and two more listening on each of the VMware virtual network adapters (VMnet1 and VMnet8, both preconfigured with their own fabricated/virtual subnets). When the INADDR_ANY receiver instance starts missing packets I see them show up on one of the VMware listeners. This is my proof that the Windows SSDP Discovery service is cycling the default adapter set for multicast transmission. I don't see this occur when the SSDP service is disabled. I assume the discovery service is doing this to catch SSDP messages on all network interfaces, although it's not clear why it has to change the system default multicast to accomplish this rather than just having multiple sockets, one on each interface in the system.

The workaround is to explicitly set which interface I configure for multicasting transmissions and listens, rather than relying on INADDR_ANY, which is the traditional way to handle multicasts that works fine on every other OS platform that is single-homed. Note that you have to explicitly set not just the transmitting interface but also the receiving side as well, because the discovery service's cycling of the default interface applies to both the default transmit interface and also which interface incoming packets are accepted for the IP multicast membership group.

Horshack
  • 111
  • 1
  • 6

0 Answers0