panjf2000 / gnet

🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.

Home Page:https://gnet.host

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Feature]: Support for SO_BINDTODEVICE?

shaunco opened this issue · comments

Description of new feature

While listening for UDP packets, it is common to listen on "0.0.0.0:123" or just ":123", which allows for the receipt of both unicast and broadcast UDP packets, however there are times where an application might want to listen on just a single network interface. Obtaining that interface's IP and then listening on "1.2.3.4:123" will only receive unicast packets, and thus the preferred mechanism is to still listen on ":123" (for v4+v6) or "0.0.0.0:123" (for v4), but to use SO_BINDTODEVICE to bind the socket to a single interface.

Once bound, tools like ss show it as 0.0.0.0%eth0:123 rather than just 0.0.0.0:123.

Scenarios for new feature

On network appliances with multiple interfaces, those interfaces are often attached to different networks - such as a network gateway that is attached to a LAN network on eth0 and the WAN network on eth1. If that device wants to listen for unicast+broadcast UDP on just the LAN interface, there is currently no way to do this with gnet (at least not that I could find).

Breaking changes or not?

No

Code snippets (optional)

For most linux/freebsd based systems:

unix.SetsockoptString(fd, unix.SOL_SOCKET, unix.SO_BINDTODEVICE, a.interfaceName)

For darwin:

iface, _ := net.InterfaceByName(devName)
unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVIF, iface.Index)
unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index)

For windows, there is example calls at https://stackoverflow.com/a/73041705 , but they'd need to be converted to go.

Alternatives for new feature

Could have separate listeners for unicast and broadcast traffic, but it gets messy.

Could listen to everything and then filter in OnTraffic, but it is much more efficient to have the kernel just bind to a single interface.

Additional context (optional)

WithMulticastInterfaceIndex() does something similar for multicast traffic.

Sounds reasonable, I'll see what we can do here.

I've done some investigation on this. SO_BINDTODEVICE is linux-specific and it seems that there are no equivalents on other platforms. IP_BOUND_IF seems like a prospective substitute on macOS but it's osx-specific and it doesn't exist on other BSD-like systems.

As for IP_RECVIF on freebsd, it doesn't look like an exact equivalent to SO_BINDTODEVICE according to its man pages.

Therefore, if we're going to do this, it looks like we're only able to do it on linux and mac.

References

Thanks for digging into this. Would that make the functions just NOPs on other platforms or does it imply platform-specific APIs?

Thanks for digging into this. Would that make the functions just NOPs on other platforms or does it imply platform-specific APIs?

If we're going to do this, API should be present on all platforms that gnet supports and returns an ErrUnsupportedOp.