Transparent Proxy Provider (again) and IPSec: should it work?

As I've mentioned multiple times, we've discovered some very annoying failures when using a TPP, including FaceTime, AirDrop, and some VPNs. (Tailscale works fine, weirdly enough.) In doing some experimentation today with FortiNet, I was able to get the TPP to work if I added the FortiNet server (which, in our case, is an amazon VM) to the TPP's excludedNetworks list.

While it is not working, the tcpdump I got for the host was:

15:15:35.584029 IP (tos 0x0, ttl 64, id 1976, offset 0, flags [none], proto UDP (17), length 412)
    192.168.43.16.55067 > ${hidden}.ipsec-msft: [udp sum ok] NONESP-encap: isakmp 1.0 msgid 00000000 cookie d66f571dcfc483ba->0000000000000000: phase 1 I ident:
    (sa: doi=ipsec situation=identity
        (p: #1 protoid=isakmp transform=2
            (t: #1 id=ike (type=lifetype value=sec)(type=lifeduration len=4 value=00015180)(type=enc value=aes)(type=keylen value=0080)(type=auth value=fde9)(type=hash value=sha1)(type=group desc value=modp2048))
            (t: #2 id=ike (type=lifetype value=sec)(type=lifeduration len=4 value=00015180)(type=enc value=aes)(type=keylen value=0100)(type=auth value=fde9)(type=hash value=sha2-256)(type=group desc value=modp2048))))
    (vid: len=16 4a131c81070358455c5728f20e95452f)
    (vid: len=16 8f8d83826d246b6fc7a8a6a428c11de8)
    (vid: len=16 439b59f8ba676c4c7737ae22eab8f582)
    (vid: len=16 4d1e0e136deafa34c4f3ea9f02ec7285)
    (vid: len=16 80d0bb3def54565ee84645d4c85ce3ee)
    (vid: len=16 7d9419a65310ca6f2c179d9215529d56)
    (vid: len=16 cd60464335df21f87cfdb2fc68b6a448)
    (vid: len=16 90cb80913ebb696e086381b5ec427b1f)
    (vid: len=16 4c53427b6d465d1b337bb755a37a7fef)
    (vid: len=16 b4f01ca951e9da8d0bafbbd34ad3044e)
    (vid: len=8 09002689dfd6b712)
    (vid: len=16 12f5f28c457168a9702d9fe274cc0100)
    (vid: len=16 afcad71368a1f1c96b8696fc77570100)
E.......@.....+.6.8c......6......oW........................|...d...........X.......(..............Q........................(..............Q.........................J.....XE\W(...E/........m$ko....(.......C.Y..glLw7."........M...m..4......r........=.TV^.FE..\......}...S..o,....R.V.....`FC5.!.|...h..H........>.in.c...B{.....LSB{mF].3{.U.z..........Q.......J..N....	.&.............Eqh.p-..t...........h...k...wW..
15:15:35.901666 IP (tos 0x0, ttl 46, id 23154, offset 0, flags [none], proto UDP (17), length 272)
    ${hidden}.ipsec-msft > 192.168.43.16.55067: [udp sum ok] NONESP-encap: isakmp 1.0 msgid 00000000 cookie d66f571dcfc483ba->d1ec3b9d2f311bf5: phase 1 R ident:
    (sa: doi=ipsec situation=identity
        (p: #1 protoid=isakmp transform=1
            (t: #1 id=ike (type=lifetype value=sec)(type=lifeduration len=4 value=00015180)(type=enc value=aes)(type=keylen value=0080)(type=auth value=fde9)(type=hash value=sha1)(type=group desc value=modp2048))))
    (vid: len=16 4a131c81070358455c5728f20e95452f)
    (vid: len=16 afcad71368a1f1c96b8696fc77570100)
    (vid: len=8 09002689dfd6b712)
    (vid: len=16 12f5f28c457168a9702d9fe274cc0204)
    (vid: len=16 4c53427b6d465d1b337bb755a37a7fef)
    (vid: len=16 8299031757a36082c6a621de00000000)
    (vid: len=16 9b15e65a871aff342666623ba5022e60)
    (vid: len=16 ca4a4cbb12eab6c58c57067c2e653786)
E...Zr......6.8c..+.......Z>.....oW.......;./1.................<...........0.......(..............Q.........................J.....XE\W(...E/........h...k...wW......	.&.............Eqh.p-..t.......LSB{mF].3{.U.z..........W.`...!............Z...4&fb;...`.....JL......W.|.e7.
15:15:35.901756 IP (tos 0x0, ttl 64, id 41586, offset 0, flags [none], proto ICMP (1), length 56)
    192.168.43.16 > ${hidden}: ICMP 192.168.43.16 udp port 55067 unreachable, length 36
	IP (tos 0x0, ttl 46, id 23154, offset 0, flags [none], proto UDP (17), length 272)
    ${hidden}.ipsec-msft > 192.168.43.16.55067: [no cksum]  [|isakmp_rfc3948]
`.....<"..:...E..8.r..@.}q..+.6.8c...Q....E...Zr......6.8c..+.........
15:15:38.904628 IP (tos 0x0, ttl 46, id 23155, offset 0, flags [none], proto UDP (17), length 272)
    ${hidden}.ipsec-msft > 192.168.43.16.55067: [udp sum ok] NONESP-encap: isakmp 1.0 msgid 00000000 cookie d66f571dcfc483ba->d1ec3b9d2f311bf5: phase 1 R ident:
    (sa: doi=ipsec situation=identity
        (p: #1 protoid=isakmp transform=1
            (t: #1 id=ike (type=lifetype value=sec)(type=lifeduration len=4 value=00015180)(type=enc value=aes)(type=keylen value=0080)(type=auth value=fde9)(type=hash value=sha1)(type=group desc value=modp2048))))
    (vid: len=16 4a131c81070358455c5728f20e95452f)
    (vid: len=16 afcad71368a1f1c96b8696fc77570100)
    (vid: len=8 09002689dfd6b712)
    (vid: len=16 12f5f28c457168a9702d9fe274cc0204)
    (vid: len=16 4c53427b6d465d1b337bb755a37a7fef)
    (vid: len=16 8299031757a36082c6a621de00000000)
    (vid: len=16 9b15e65a871aff342666623ba5022e60)
    (vid: len=16 ca4a4cbb12eab6c58c57067c2e653786)
E...Zs......6.8c..+.......Z>.....oW.......;./1.................<...........0.......(..............Q.........................J.....XE\W(...E/........h...k...wW......	.&.............Eqh.p-..t.......LSB{mF].3{.U.z..........W.`...!............Z...4&fb;...`.....JL......W.|.e7.
15:15:38.904763 IP (tos 0x0, ttl 64, id 8956, offset 0, flags [none], proto ICMP (1), length 56)
    192.168.43.16 > ${hidden}: ICMP 192.168.43.16 udp port 55067 unreachable, length 36
	IP (tos 0x0, ttl 46, id 23155, offset 0, flags [none], proto UDP (17), length 272)
    ${hidden}.ipsec-msft > 192.168.43.16.55067: [no cksum]  [|isakmp_rfc3948]
`.....<"..:...E..8"...@.....+.6.8c...Q....E...Zs......6.8c..+.........

So, given that, I tried adding

    let msftIPSecHost = NWHostEndpoint(hostname: "", port: "4500")
    let msftIPSecRule = NENetworkRule(destinationNetwork: msftIPSecHost, prefix: 0, protocol: .any)
    settings.excludedNetworkRules = [msftIPSecRule]

and... it worked. At least, the fortinet client worked, and AirDrop transmission worked. Note that I never saw the flows for port 4500 in handleNewUDPFlow(:initialRemoteEndpoint:) -- just having a UDP rule that would intercept them seems to have caused it to fail.

Anyone encountered this, or have an explanation? (I am now trying it in our actual product to see how it works.)

Another fun thing: the documentation is wrong about the NWHostEndpoint and NENetworkRule: if you use a wildcard (as my code above does), it ignores the port. So my code there doesn't quite work; I ended up adding 4 separate hosts, with a corresponding rule each:

        let udpHost_1 = NWHostEndpoint(hostname:"0.0.0.0", port: "4500")
        let udpHost_2 = NWHostEndpoint(hostname:"255.0.0.0", port: "4500")
        let udpHost_3 = NWHostEndpoint(hostname:"0::0", port: "4500")
        let udpHost_4 = NWHostEndpoint(hostname:"ff::ff", port: "4500")
        let ipsecRule_1 = NENetworkRule(destinationNetwork: udpHost_1, prefix: 1, protocol: .UDP)
        let ipsecRule_2 = NENetworkRule(destinationNetwork: udpHost_2, prefix: 1, protocol: .UDP)
        let ipsecRule_3 = NENetworkRule(destinationNetwork: udpHost_3, prefix: 1, protocol: .UDP)
        let ipsecRule_4 = NENetworkRule(destinationNetwork: udpHost_4, prefix: 1, protocol: .UDP)

This is really really ugly, but it does appear to cause it to use look at the port part of the host/rule.

Another fun thing: the documentation is wrong about the NWHostEndpoint and NENetworkRule: if you use a wildcard (as my code above does), .

Did you need to set your address in endpoint as a wildcard address? For example:

NWHostEndpoint(hostname: "0.0.0.0", port: "4500")

Matt Eaton - Networking

I think I tried that and it didn't work; I'd have to confirm by rebuilding it, but the documentation says that "" is valid wildcard, and it did not work.

Transparent Proxy Provider (again) and IPSec: should it work?
 
 
Q