-
Notifications
You must be signed in to change notification settings - Fork 0
nat/natpmp: add low-level Conn and Message APIs #3
Description
Per the discussions with @danderson in #1, there is a need to enable sending/receiving NAT-PMP (and later PCP) messages over a single multiplexed UDP socket.
I think the best way to do this is to add a low-level Conn API (inspired by my NDP package) that looks something like so:
package natpmp
type Conn struct {
// TODO: do or do not embed this directly to allow access to all deadline/raw byte I/O methods?
net.PacketConn
}
func NewConn(pc net.PacketConn) (*Conn, error) {
// Setup logic probably using x/net/ipv4. We still need to think about the multicast group case
// where a NAT gateway can notify us of its new external IP.
}
type Message interface {
Op() uint8
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
}
// All messages implement the Message interface.
type ExternalAddressRequest struct{}
type ExternalAddress struct{}
// other messages
// Marshaling to/from bytes while also dealing with message headers and I/O errors.
func ParseMessage(b []byte) (Message, error) {}
func MarshalMessage(m Message) ([]byte, error) {}
// Convenient APIs for dealing with Messages directly, while the underlying Conn also permits raw byte I/O
func (c *Conn) SendMessage(m Message, addr net.Addr) error {}
func (c *Conn) ReceiveMessage() (Message, net.Addr, error) {}The existing Client can make use of this API in a very concise way and keep all the existing serialization and backoff/retry logic. It's unclear to me if any of that logic should live in Conn directly, but I'm leaning toward keeping it out.
For the Tailscale netcheck use case, it'd be easy to probe for NAT-PMP (and later PCP) using Conn.SendMessage, and then messages could be received using a raw Conn.ReadFrom combined with a call to ParseMessage.
Overall I think this approach provides significant flexibility while also allowing a nice low and high-level APIs. Thoughts, @danderson?