The "advances in networking" talks for WWDC had this example of using NWEthernetChannel to monitor a custom protocol.
import Foundation
Import Network
let path = NWPathMonitor(requiredInterfaceType: .wiredEthernet).currentPath
guard let interface = path.availableInterfaces.first else {
fatalError("not connected to Internet")
}
let channel = NWEthernetChannel(on: interface, etherType: 0xB26E)
For my application I need to use a NWEthernetChannel to monitor a custom protocol* on an Ethernet link which does not have IP Internet connectivity (but it does have physical link to a switch). NWPath appears to only give me a NWInterface struct if it is a valid path to the Internet.
How can I get a list of NWInterface Structs on the Mac without having a valid Internet path?
In my particular use case I'm only interested in .wiredEthernet.
Darrell
* I'm trying to use Network.framework to receive LLDP (link-layer discovery protocol) and CDP (Cisco discovery protocol) traffic when I'm plugged into an ethernet switch, which may or may not have Internet connectivity.
This is an interesting edge case. I had a chat with the Network framework team about this and there is no direct way to do what you want. This kinda makes sense given Network framework’s original focus on transport protocols, but it’s clearly no longer sufficient. We would appreciate you filing a bug about this, explaining what you’re doing and where you got stuck. Please post your bug number, just for the record.
In the meantime, they suggested a workaround that seems viable. On Apple platforms all interfaces get a link-local IPv6 address. If you create an UDP connection (
NWConnection
) to that address, the resulting path contain’s the interface object you need.
Pasted in below is some code that shows this in action. I tested it as follows:
I started with a MacBook running 10.15.2.
I connected the Wi-Fi interface to the wider Internet.
I attached a Thunderbolt Ethernet dongle. This became
.en7
I connected the Ethernet cable to the Ethernet port on another Mac. The other Mac isn’t relevant here, I just needed an active port that would bring up the link but not route any traffic.
I ran the program below. As you see, the UDP connection’s path update handler is called with an interface object for
. You should be able to use that for youren7
.NWEthernetChannel
Please try this out and let me know how you get along.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation
import Network
func firstV6AddrForInterface(name: String) -> String? {
var addrList: UnsafeMutablePointer<ifaddrs>? = nil
guard
getifaddrs(&addrList) == 0,
let first = addrList
else { return nil }
defer { freeifaddrs(addrList) }
return sequence(first: first, next: { $0.pointee.ifa_next })
.first { addr -> Bool in
guard
let sa = addr.pointee.ifa_addr,
sa.pointee.sa_family == AF_INET6,
String(cString: addr.pointee.ifa_name) == name
else {
return false
}
return true
}
.flatMap { addr -> String? in
var name = [CChar](repeating: 0, count: Int(NI_MAXHOST))
let err = getnameinfo(
addr.pointee.ifa_addr,
socklen_t(addr.pointee.ifa_addr.pointee.sa_len),
&name, socklen_t(name.count),
nil,
0,
NI_NUMERICHOST | NI_NUMERICSERV
)
guard err == 0 else {
return nil
}
return String(cString: name)
}
}
func main() {
guard let en7Addr = firstV6AddrForInterface(name: "en7") else {
fatalError()
}
print("starting with \(en7Addr)…")
let conn = NWConnection(host: .init(en7Addr), port: 12345, using: .udp)
conn.pathUpdateHandler = { path in
print(path.availableInterfaces) // prints: [en7]
}
conn.start(queue: .main)
dispatchMain()
}
main()