Sending UDP (SSDP) request on an interface hidden by VPN

14 views
Skip to first unread message

Thomas Tempelmann

unread,
Apr 21, 2024, 12:54:23 PMApr 21
to CocoaAsyncSocket
I'm writing an open-sourced SSDP Browser for macOS (https://github.com/tempelmann/SSDP-Browser/) and have run into a problem when the Mac uses a VPN that routes all traffic thru the tunnel: In that case, sending a UDP request to 239.255.255.250 on the default interface doesn't work - I get the error 65 (network not reachable).

However:

1. The Mac also has another interface, and sending the same SSDP msg to that interface works.
2. I can connect to any device on the problematic interface by other means (e.g. I can ping devices or mount network shares on the subnet).
3. When I stop the VPN, then I can, of course, send the UDP package on the interface just fine.

Is there something I can do to make the UDP transmission work?

More details follow:

Below is the layout of the interfaces when not connected to the VPN (and below that when it's connected).

The 192.168.4.2 interface is the one that's causing the problem with VPN, whereas I can send the UDP packet on the 192.168.61.128 interface no matter whether VPN is connected or not.

The sending code is basically doing this:

- instantiate GCDAsyncUdpSocket object
- enableReusePort:YES
- setIPv6Enabled:NO (doesn't matter, though)
- bindToPort:0 interface:"192.168.4.2"
- beginReceiving
- sendData:… toHost:"239.255.255.250" port:1900

The sendData then leads to errno=65.

Any idea how to make this work?

$ netstat -nr
Routing tables

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            192.168.4.1        UGSc           13        0     en0
default            192.168.61.1       UGScI           1        0     en1
127                127.0.0.1          UCS             0        0     lo0
127.0.0.1          127.0.0.1          UH              3    85887     lo0
169.254            link#4             UCS             0        0     en0
169.254            link#5             UCSI            0        0     en1
192.168.4          link#4             UCS             3        0     en0
192.168.4.1/32     link#4             UCS             1        0     en0
192.168.4.1        c:72:74:6:e1:1c    UHLWIir        16     3474     en0   1182
192.168.4.2/32     link#4             UCS             0        0     en0
192.168.4.6        90:9:d0:25:ac:40   UHLWIi          2    55759     en0   1124
192.168.4.203      dc:56:e7:4e:fb:90  UHLWI           0        0     en0   1178
192.168.4.206      ac:bc:32:eb:73:cd  UHLWIi          2   307574     en0    433
192.168.61         link#5             UCS             0        0     en1
192.168.61.1/32    link#5             UCS             1        0     en1
192.168.61.1       3c:37:12:e3:b5:e0  UHLWIir         2     2312     en1   1177
192.168.61.128/32  link#5             UCS             1        0     en1
224.0.0            link#4             UmCS            1        0     en0
224.0.0            link#5             UmCSI           0        0     en1
224.0.0.251        1:0:5e:0:0:fb      UHmLWI          0        0     en0
255.255.255.255/32 link#4             UCS             0        0     en0
255.255.255.255/32 link#5             UCSI            0        0     en1


And with VPN:

$ netstat -nr
Routing tables

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
0/1                198.167.206.65     UGSc            5        0   utun0
default            192.168.4.1        UGSc            1        0     en0
default            192.168.61.1       UGScI           1        0     en1
127                127.0.0.1          UCS             0        0     lo0
127.0.0.1          127.0.0.1          UH              7    63554     lo0
128.0/1            198.167.206.65     UGSc            8        0   utun0
169.254            link#4             UCS             0        0     en0
169.254            link#5             UCSI            0        0     en1
192.168.4          link#4             UCS             2        0     en0
192.168.4.1/32     link#4             UCS             1        0     en0
192.168.4.1        c:72:74:6:e1:1c    UHLWIir         7     3341     en0   1193
192.168.4.2/32     link#4             UCS             0        0     en0
192.168.4.6        90:9:d0:25:ac:40   UHLWIi          2    55024     en0    962
192.168.4.206      ac:bc:32:eb:73:cd  UHLWIi          2   252101     en0    682
192.168.61         link#5             UCS             1        0     en1
192.168.61.1/32    link#5             UCS             1        0     en1
192.168.61.1       3c:37:12:e3:b5:e0  UHLWIir         2     2186     en1   1017
192.168.61.128/32  link#5             UCS             0        0     en1
198.167.192.27/32  192.168.4.1        UGSc            1        0     en0
198.167.206.64/27  198.167.206.74     UGSc           14        0   utun0
198.167.206.74     198.167.206.74     UH              1        0   utun0
224.0.0            link#4             UmCS            1        0     en0
224.0.0            link#5             UmCSI           0        0     en1
224.0.0.251        1:0:5e:0:0:fb      UHmLWI          0        0     en0
255.255.255.255/32 link#4             UCS             0        0     en0
255.255.255.255/32 link#5             UCSI            0        0     en1

Thomas Tempelmann

unread,
Apr 21, 2024, 2:06:51 PMApr 21
to CocoaAsyncSocket
Actually, after some trial-and-error, I found that adding this call after binding seems to resolve the issue:

  [socket sendIPv4MulticastOnInterface:interface error:nil]

(with analogous call when using IPv6)

Is that the correct method? Or am I getting an undesired side effect here? I have trouble identifying what this function is meant for.

Also, two more things:

1. When I query the Mac's interfaces, those for IPv6 are in the form `...%en0`, i.e. the IPv6 address is joined by the interface device name, separate by "%". I assume that when I call the bindToPort and the sendIPv4MulticastOnInterface methods, I have to pass just the part past the "%", such as "en0" to both calls for the interface name, right?
2. I found that the CFRead/WriteStream related features are only provided when building for iOS. I found that the code compiles just as well on macOS, though. What's the reason for only enabling it on iOS, then?

Thomas

Reply all
Reply to author
Forward
0 new messages