diff options
Diffstat (limited to 'src/conn.go')
-rw-r--r-- | src/conn.go | 109 |
1 files changed, 77 insertions, 32 deletions
diff --git a/src/conn.go b/src/conn.go index 2cf588d..5b40a23 100644 --- a/src/conn.go +++ b/src/conn.go @@ -2,10 +2,35 @@ package main import ( "errors" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" "net" - "time" ) +/* A Bind handles listening on a port for both IPv6 and IPv4 UDP traffic + */ +type Bind interface { + SetMark(value uint32) error + ReceiveIPv6(buff []byte) (int, Endpoint, error) + ReceiveIPv4(buff []byte) (int, Endpoint, error) + Send(buff []byte, end Endpoint) error + Close() error +} + +/* An Endpoint maintains the source/destination caching for a peer + * + * dst : the remote address of a peer ("endpoint" in uapi terminology) + * src : the local address from which datagrams originate going to the peer + */ +type Endpoint interface { + ClearSrc() // clears the source address + SrcToString() string // returns the local source address (ip:port) + DstToString() string // returns the destination address (ip:port) + DstToBytes() []byte // used for mac2 cookie calculations + DstIP() net.IP + SrcIP() net.IP +} + func parseEndpoint(s string) (*net.UDPAddr, error) { // ensure that the host is an IP address @@ -27,63 +52,83 @@ func parseEndpoint(s string) (*net.UDPAddr, error) { return addr, err } -func updateUDPConn(device *Device) error { +/* Must hold device and net lock + */ +func unsafeCloseUDPListener(device *Device) error { + var err error + netc := &device.net + if netc.bind != nil { + err = netc.bind.Close() + netc.bind = nil + } + return err +} + +// must inform all listeners +func UpdateUDPListener(device *Device) error { + device.mutex.Lock() + defer device.mutex.Unlock() + netc := &device.net netc.mutex.Lock() defer netc.mutex.Unlock() - // close existing connection + // close existing sockets - if netc.conn != nil { - netc.conn.Close() - netc.conn = nil - - // We need for that fd to be closed in all other go routines, which - // means we have to wait. TODO: find less horrible way of doing this. - time.Sleep(time.Second / 2) + if err := unsafeCloseUDPListener(device); err != nil { + return err } - // open new connection + // assumption: netc.update WaitGroup should be exactly 1 + + // open new sockets if device.tun.isUp.Get() { - // listen on new address + device.log.Debug.Println("UDP bind updating") - conn, err := net.ListenUDP("udp", netc.addr) + // bind to new port + + var err error + netc.bind, netc.port, err = CreateBind(netc.port) if err != nil { + netc.bind = nil return err } - // set fwmark + // set mark - err = setMark(netc.conn, netc.fwmark) + err = netc.bind.SetMark(netc.fwmark) if err != nil { return err } - // retrieve port (may have been chosen by kernel) + // clear cached source addresses + + for _, peer := range device.peers { + peer.mutex.Lock() + if peer.endpoint != nil { + peer.endpoint.ClearSrc() + } + peer.mutex.Unlock() + } - addr := conn.LocalAddr() - netc.conn = conn - netc.addr, _ = net.ResolveUDPAddr( - addr.Network(), - addr.String(), - ) + // decrease waitgroup to 0 - // notify goroutines + go device.RoutineReceiveIncomming(ipv4.Version, netc.bind) + go device.RoutineReceiveIncomming(ipv6.Version, netc.bind) - signalSend(device.signal.newUDPConn) + device.log.Debug.Println("UDP bind has been updated") } return nil } -func closeUDPConn(device *Device) { - netc := &device.net - netc.mutex.Lock() - if netc.conn != nil { - netc.conn.Close() - } - netc.mutex.Unlock() - signalSend(device.signal.newUDPConn) +func CloseUDPListener(device *Device) error { + device.mutex.Lock() + device.net.mutex.Lock() + err := unsafeCloseUDPListener(device) + device.net.mutex.Unlock() + device.mutex.Unlock() + return err } |