diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-07-11 10:45:30 +0900 |
---|---|---|
committer | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2017-07-18 15:11:01 +0900 |
commit | 178e65f8f113da82f8dfcc56b5fa7682da1bd932 (patch) | |
tree | 52b423ccf52b64d14b278fb39d8e6a3e399488b2 | |
parent | eb95f000bf4ab70c2dad7589e58045fbce1addf1 (diff) |
server: Set outgoing TTL before dialing
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
-rw-r--r-- | server/fsm.go | 46 | ||||
-rw-r--r-- | server/server.go | 4 | ||||
-rw-r--r-- | server/sockopt.go | 60 | ||||
-rw-r--r-- | server/sockopt_bsd.go | 115 | ||||
-rw-r--r-- | server/sockopt_linux.go | 187 | ||||
-rw-r--r-- | server/sockopt_openbsd.go | 114 | ||||
-rw-r--r-- | server/util.go | 31 |
7 files changed, 380 insertions, 177 deletions
diff --git a/server/fsm.go b/server/fsm.go index 953e2f7d..55a3aa0c 100644 --- a/server/fsm.go +++ b/server/fsm.go @@ -329,28 +329,32 @@ func (fsm *FSM) connectLoop() error { if fsm.pConf.Transport.Config.RemotePort != 0 { port = int(fsm.pConf.Transport.Config.RemotePort) } - host := net.JoinHostPort(addr, strconv.Itoa(port)) - // check if LocalAddress has been configured - laddr := fsm.pConf.Transport.Config.LocalAddress + laddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(fsm.pConf.Transport.Config.LocalAddress, "0")) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": fsm.pConf.State.NeighborAddress, + }).Warn("failed to resolve local address: %s", err) + return + } var conn net.Conn - var err error - if fsm.pConf.Config.AuthPassword != "" { - deadline := (MIN_CONNECT_RETRY - 1) * 1000 // msec - conn, err = DialTCPTimeoutWithMD5Sig(addr, port, laddr, fsm.pConf.Config.AuthPassword, deadline) - } else { - lhost := net.JoinHostPort(laddr, "0") - ltcpaddr, e := net.ResolveTCPAddr("tcp", lhost) - if e != nil { - log.WithFields(log.Fields{ - "Topic": "Peer", - "Key": fsm.pConf.State.NeighborAddress, - }).Warnf("failed to resolve ltcpaddr: %s", e) - return + d := TCPDialer{ + Dialer: net.Dialer{ + LocalAddr: laddr, + Timeout: time.Duration(MIN_CONNECT_RETRY-1) * time.Second, + }, + AuthPassword: fsm.pConf.Config.AuthPassword, + } + if fsm.pConf.TtlSecurity.Config.Enabled { + d.Ttl = 255 + d.TtlMin = fsm.pConf.TtlSecurity.Config.TtlMin + } else if fsm.pConf.Config.PeerAs != 0 && fsm.pConf.Config.PeerType == config.PEER_TYPE_EXTERNAL { + d.Ttl = 1 + if fsm.pConf.EbgpMultihop.Config.Enabled { + d.Ttl = fsm.pConf.EbgpMultihop.Config.MultihopTtl } - d := net.Dialer{LocalAddr: ltcpaddr, Timeout: time.Duration(MIN_CONNECT_RETRY-1) * time.Second} - conn, err = d.Dial("tcp", host) } - + conn, err = d.DialTCP(addr, port) if err == nil { select { case fsm.connCh <- conn: @@ -505,7 +509,7 @@ func (h *FSMHandler) active() (bgp.FSMState, FsmStateReason) { ttl = int(fsm.pConf.Transport.Config.Ttl) } if ttl != 0 { - if err := SetTcpTTLSockopts(conn.(*net.TCPConn), ttl); err != nil { + if err := SetTcpTTLSockopt(conn.(*net.TCPConn), ttl); err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, @@ -514,7 +518,7 @@ func (h *FSMHandler) active() (bgp.FSMState, FsmStateReason) { } } if ttlMin != 0 { - if err := SetTcpMinTTLSockopts(conn.(*net.TCPConn), ttlMin); err != nil { + if err := SetTcpMinTTLSockopt(conn.(*net.TCPConn), ttlMin); err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, diff --git a/server/server.go b/server/server.go index 2dda4dff..61502ced 100644 --- a/server/server.go +++ b/server/server.go @@ -1723,7 +1723,7 @@ func (server *BgpServer) addNeighbor(c *config.Neighbor) error { if server.bgpConfig.Global.Config.Port > 0 { for _, l := range server.Listeners(addr) { if c.Config.AuthPassword != "" { - if err := SetTcpMD5SigSockopts(l, addr, c.Config.AuthPassword); err != nil { + if err := SetTcpMD5SigSockopt(l, addr, c.Config.AuthPassword); err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": addr, @@ -1825,7 +1825,7 @@ func (server *BgpServer) deleteNeighbor(c *config.Neighbor, code, subcode uint8) return fmt.Errorf("Can't delete a peer configuration for %s", addr) } for _, l := range server.Listeners(addr) { - if err := SetTcpMD5SigSockopts(l, addr, ""); err != nil { + if err := SetTcpMD5SigSockopt(l, addr, ""); err != nil { log.WithFields(log.Fields{ "Topic": "Peer", "Key": addr, diff --git a/server/sockopt.go b/server/sockopt.go index 2f6a8bb6..e053bef0 100644 --- a/server/sockopt.go +++ b/server/sockopt.go @@ -19,20 +19,68 @@ package server import ( "fmt" "net" + + log "github.com/sirupsen/logrus" ) -func SetTcpMD5SigSockopts(l *net.TCPListener, address string, key string) error { - return fmt.Errorf("md5 not supported") +func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error { + return fmt.Errorf("setting md5 is not supported") } -func SetTcpTTLSockopts(conn *net.TCPConn, ttl int) error { +func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error { return fmt.Errorf("setting ttl is not supported") } -func SetTcpMinTTLSockopts(conn *net.TCPConn, ttl int) error { +func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { return fmt.Errorf("setting min ttl is not supported") } -func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec int) (*net.TCPConn, error) { - return nil, fmt.Errorf("md5 active connection unsupported") +type TCPDialer struct { + net.Dialer + + // MD5 authentication password. + AuthPassword string + + // The TTL value to set outgoing connection. + Ttl uint8 + + // The minimum TTL value for incoming packets. + TtlMin uint8 +} + +func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) { + if d.AuthPassword != "" { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting md5 for active connection is not supported") + } + if d.Ttl != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting ttl for active connection is not supported") + } + if d.TtlMin != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting min ttl for active connection is not supported") + } + + raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port))) + if err != nil { + return nil, fmt.Errorf("invalid remote address: %s", err) + } + laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String()) + if err != nil { + return nil, fmt.Errorf("invalid local address: %s", err) + } + + dialer := net.Dialer{LocalAddr: laddr, Timeout: d.Timeout} + conn, err := dialer.Dial("tcp", raddr.String()) + if err != nil { + return nil, err + } + return conn.(*net.TCPConn), nil } diff --git a/server/sockopt_bsd.go b/server/sockopt_bsd.go index a9f9d61f..20644775 100644 --- a/server/sockopt_bsd.go +++ b/server/sockopt_bsd.go @@ -20,8 +20,9 @@ import ( "fmt" "net" "os" - "strings" "syscall" + + log "github.com/sirupsen/logrus" ) const ( @@ -29,59 +30,109 @@ const ( IPV6_MINHOPCOUNT = 73 // Generalized TTL Security Mechanism (RFC5082) ) -func SetTcpMD5SigSockopts(l *net.TCPListener, address string, key string) error { +func setsockoptTcpMD5Sig(fd int, address string, key string) error { + // always enable and assumes that the configuration is done by setkey() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, 1)) +} + +func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error { fi, err := l.File() defer fi.Close() - if err != nil { return err } - - if l, err := net.FileListener(fi); err == nil { - defer l.Close() - } - - // always enable and assumes that the configuration is done by - // setkey() - if err := syscall.SetsockoptInt(int(fi.Fd()), - syscall.IPPROTO_TCP, TCP_MD5SIG, 1); err != nil { - return err - } - return nil -} - -func setTcpSockoptInt(conn *net.TCPConn, level int, name int, value int) error { - fi, err := conn.File() - defer fi.Close() + fl, err := net.FileListener(fi) + defer fl.Close() if err != nil { return err } - if conn, err := net.FileConn(fi); err == nil { - defer conn.Close() - } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fi.Fd()), level, name, value)) + return setsockoptTcpMD5Sig(int(fi.Fd()), address, key) } -func SetTcpTTLSockopts(conn *net.TCPConn, ttl int) error { +func setsockoptIpTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_TTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = syscall.IPV6_UNICAST_HOPS } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) +} + +func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpTtl(int(fi.Fd()), family, ttl) } -func SetTcpMinTTLSockopts(conn *net.TCPConn, ttl int) error { +func setsockoptIpMinTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_MINTTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = IPV6_MINHOPCOUNT } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) } -func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec int) (*net.TCPConn, error) { - return nil, fmt.Errorf("md5 active connection unsupported") +func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpMinTtl(int(fi.Fd()), family, ttl) +} + +type TCPDialer struct { + net.Dialer + + // MD5 authentication password. + AuthPassword string + + // The TTL value to set outgoing connection. + Ttl uint8 + + // The minimum TTL value for incoming packets. + TtlMin uint8 +} + +func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) { + if d.AuthPassword != "" { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting md5 for active connection is not supported") + } + if d.Ttl != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting ttl for active connection is not supported") + } + if d.TtlMin != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting min ttl for active connection is not supported") + } + + raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port))) + if err != nil { + return nil, fmt.Errorf("invalid remote address: %s", err) + } + laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String()) + if err != nil { + return nil, fmt.Errorf("invalid local address: %s", err) + } + + dialer := net.Dialer{LocalAddr: laddr, Timeout: d.Timeout} + conn, err := dialer.Dial("tcp", raddr.String()) + if err != nil { + return nil, err + } + return conn.(*net.TCPConn), nil } diff --git a/server/sockopt_linux.go b/server/sockopt_linux.go index 0dc4cd5f..c251f8fd 100644 --- a/server/sockopt_linux.go +++ b/server/sockopt_linux.go @@ -20,7 +20,6 @@ import ( "fmt" "net" "os" - "strings" "syscall" "unsafe" ) @@ -56,143 +55,159 @@ func buildTcpMD5Sig(address string, key string) (tcpmd5sig, error) { return t, nil } -func SetTcpMD5SigSockopts(l *net.TCPListener, address string, key string) error { - t, _ := buildTcpMD5Sig(address, key) - fi, err := l.File() - defer fi.Close() +func setsockoptTcpMD5Sig(fd int, address string, key string) error { + t, err := buildTcpMD5Sig(address, key) if err != nil { return err } - if l, err := net.FileListener(fi); err == nil { - defer l.Close() - } b := *(*[unsafe.Sizeof(t)]byte)(unsafe.Pointer(&t)) - if err := syscall.SetsockoptString(int(fi.Fd()), - syscall.IPPROTO_TCP, TCP_MD5SIG, - string(b[:])); err != nil { - return err - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptString(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, string(b[:]))) } -func setTcpSockoptInt(conn *net.TCPConn, level int, name int, value int) error { - fi, err := conn.File() +func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error { + fi, err := l.File() defer fi.Close() if err != nil { return err } - if conn, err := net.FileConn(fi); err == nil { - defer conn.Close() + fl, err := net.FileListener(fi) + defer fl.Close() + if err != nil { + return err } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fi.Fd()), level, name, value)) + return setsockoptTcpMD5Sig(int(fi.Fd()), address, key) } -func SetTcpTTLSockopts(conn *net.TCPConn, ttl int) error { +func setsockoptIpTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_TTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = syscall.IPV6_UNICAST_HOPS } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) } -func SetTcpMinTTLSockopts(conn *net.TCPConn, ttl int) error { +func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpTtl(int(fi.Fd()), family, ttl) +} + +func setsockoptIpMinTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_MINTTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = IPV6_MINHOPCOUNT } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) } -func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec int) (*net.TCPConn, error) { +func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpMinTtl(int(fi.Fd()), family, ttl) +} + +type TCPDialer struct { + net.Dialer + + // MD5 authentication password. + AuthPassword string + + // The TTL value to set outgoing connection. + Ttl uint8 + + // The minimum TTL value for incoming packets. + TtlMin uint8 +} + +func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) { var family int var ra, la syscall.Sockaddr - ip, err := net.ResolveIPAddr("ip", host) + raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port))) if err != nil { - return nil, fmt.Errorf("invalid ip: %s", err) - } - l, err := net.ResolveIPAddr("ip", localAddr) - if l == nil { - return nil, fmt.Errorf("invalid local ip: %s", err) + return nil, fmt.Errorf("invalid remote address: %s", err) } - if (ip.IP.To4() != nil) != (l.IP.To4() != nil) { - return nil, fmt.Errorf("remote and local ip address family is not same") + laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String()) + if err != nil { + return nil, fmt.Errorf("invalid local address: %s", err) } - switch { - case ip.IP.To4() != nil: + if raddr.IP.To4() != nil { family = syscall.AF_INET - i := &syscall.SockaddrInet4{ - Port: port, - } - for idx, _ := range i.Addr { - i.Addr[idx] = ip.IP.To4()[idx] - } - ra = i - j := &syscall.SockaddrInet4{} - for idx, _ := range j.Addr { - j.Addr[idx] = l.IP.To4()[idx] - } - la = j - default: + rsockaddr := &syscall.SockaddrInet4{Port: port} + copy(rsockaddr.Addr[:], raddr.IP.To4()) + ra = rsockaddr + lsockaddr := &syscall.SockaddrInet4{} + copy(lsockaddr.Addr[:], laddr.IP.To4()) + la = lsockaddr + } else { family = syscall.AF_INET6 - i := &syscall.SockaddrInet6{ - Port: port, - } - for idx, _ := range i.Addr { - i.Addr[idx] = ip.IP[idx] - } - ra = i + rsockaddr := &syscall.SockaddrInet6{Port: port} + copy(rsockaddr.Addr[:], raddr.IP.To16()) + ra = rsockaddr var zone uint32 - if l.Zone != "" { - intf, err := net.InterfaceByName(l.Zone) - if err != nil { + if laddr.Zone != "" { + if intf, err := net.InterfaceByName(laddr.Zone); err != nil { return nil, err + } else { + zone = uint32(intf.Index) } - zone = uint32(intf.Index) - } - j := &syscall.SockaddrInet6{ - ZoneId: zone, - } - for idx, _ := range j.Addr { - j.Addr[idx] = l.IP[idx] } - la = j + lsockaddr := &syscall.SockaddrInet6{ZoneId: zone} + copy(lsockaddr.Addr[:], laddr.IP.To16()) + la = lsockaddr } - sotype := syscall.SOCK_STREAM | syscall.SOCK_CLOEXEC | syscall.SOCK_NONBLOCK + + sockType := syscall.SOCK_STREAM | syscall.SOCK_CLOEXEC | syscall.SOCK_NONBLOCK proto := 0 - fd, err := syscall.Socket(family, sotype, proto) + fd, err := syscall.Socket(family, sockType, proto) if err != nil { return nil, err } - fi := os.NewFile(uintptr(fd), "") - defer fi.Close() - - t, err := buildTcpMD5Sig(host, key) - if err != nil { - return nil, err - } - b := *(*[unsafe.Sizeof(t)]byte)(unsafe.Pointer(&t)) - if err := syscall.SetsockoptString(int(fi.Fd()), - syscall.IPPROTO_TCP, TCP_MD5SIG, - string(b[:])); err != nil { - return nil, os.NewSyscallError("setsockopt", err) - } if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1); err != nil { return nil, os.NewSyscallError("setsockopt", err) } + if err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1); err != nil { return nil, os.NewSyscallError("setsockopt", err) } + + if d.AuthPassword != "" { + if err = setsockoptTcpMD5Sig(fd, addr, d.AuthPassword); err != nil { + return nil, err + } + } + + if d.Ttl != 0 { + if err = setsockoptIpTtl(fd, family, int(d.Ttl)); err != nil { + return nil, err + } + } + + if d.TtlMin != 0 { + if err = setsockoptIpMinTtl(fd, family, int(d.Ttl)); err != nil { + return nil, err + } + } + if err = syscall.Bind(fd, la); err != nil { return nil, os.NewSyscallError("bind", err) } - tcpconn := func(fi *os.File) (*net.TCPConn, error) { + newTCPConn := func(fd int) (*net.TCPConn, error) { + fi := os.NewFile(uintptr(fd), "") + defer fi.Close() + conn, err := net.FileConn(fi) return conn.(*net.TCPConn), err } @@ -202,7 +217,7 @@ func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: // do timeout handling case nil, syscall.EISCONN: - return tcpconn(fi) + return newTCPConn(fd) default: return nil, os.NewSyscallError("connect", err) } @@ -223,7 +238,7 @@ func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec } for { - nevents, e := syscall.EpollWait(epfd, events, msec) + nevents, e := syscall.EpollWait(epfd, events, int(d.Timeout/1000000) /*msec*/) if e != nil { return nil, e } @@ -237,7 +252,7 @@ func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec switch err := syscall.Errno(nerr); err { case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: case syscall.Errno(0), syscall.EISCONN: - return tcpconn(fi) + return newTCPConn(fd) default: return nil, os.NewSyscallError("getsockopt", err) } diff --git a/server/sockopt_openbsd.go b/server/sockopt_openbsd.go index 6dd26e59..1adbaf94 100644 --- a/server/sockopt_openbsd.go +++ b/server/sockopt_openbsd.go @@ -19,12 +19,12 @@ package server import ( "encoding/binary" "fmt" - log "github.com/sirupsen/logrus" "net" "os" - "strings" "syscall" "unsafe" + + log "github.com/sirupsen/logrus" ) const ( @@ -352,21 +352,9 @@ const ( IPV6_MINHOPCOUNT = 73 // Generalized TTL Security Mechanism (RFC5082) ) -func SetTcpMD5SigSockopts(l *net.TCPListener, address string, key string) error { - fi, err := l.File() - defer fi.Close() - - if err != nil { - return err - } - - if l, err := net.FileListener(fi); err == nil { - defer l.Close() - } - - t := int(1) - if e := syscall.SetsockoptInt(int(fi.Fd()), syscall.IPPROTO_TCP, TCP_MD5SIG, t); e != nil { - return e +func setsockoptTcpMD5Sig(fd int, address string, key string) error { + if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, 1); err != nil { + return os.NewSyscallError("setsockopt", err) } if len(key) > 0 { return saAdd(address, key) @@ -374,38 +362,104 @@ func SetTcpMD5SigSockopts(l *net.TCPListener, address string, key string) error return saDelete(address) } -func setTcpSockoptInt(conn *net.TCPConn, level int, name int, value int) error { - fi, err := conn.File() +func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error { + fi, err := l.File() defer fi.Close() if err != nil { return err } - if conn, err := net.FileConn(fi); err == nil { - defer conn.Close() + fl, err := net.FileListener(fi) + defer fl.Close() + if err != nil { + return err } - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fi.Fd()), level, name, value)) + return setsockoptTcpMD5Sig(int(fi.Fd()), address, key) } -func SetTcpTTLSockopts(conn *net.TCPConn, ttl int) error { +func setsockoptIpTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_TTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = syscall.IPV6_UNICAST_HOPS } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) } -func SetTcpMinTTLSockopts(conn *net.TCPConn, ttl int) error { +func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpTtl(int(fi.Fd()), family, ttl) +} + +func setsockoptIpMinTtl(fd int, family int, value int) error { level := syscall.IPPROTO_IP name := syscall.IP_MINTTL - if strings.Contains(conn.RemoteAddr().String(), "[") { + if family == syscall.AF_INET6 { level = syscall.IPPROTO_IPV6 name = IPV6_MINHOPCOUNT } - return setTcpSockoptInt(conn, level, name, ttl) + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value)) } -func DialTCPTimeoutWithMD5Sig(host string, port int, localAddr, key string, msec int) (*net.TCPConn, error) { - return nil, fmt.Errorf("md5 active connection unsupported") +func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { + fi, family, err := extractFileAndFamilyFromTCPConn(conn) + defer fi.Close() + if err != nil { + return err + } + return setsockoptIpMinTtl(int(fi.Fd()), family, ttl) +} + +type TCPDialer struct { + net.Dialer + + // MD5 authentication password. + AuthPassword string + + // The TTL value to set outgoing connection. + Ttl uint8 + + // The minimum TTL value for incoming packets. + TtlMin uint8 +} + +func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) { + if d.AuthPassword != "" { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting md5 for active connection is not supported") + } + if d.Ttl != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting ttl for active connection is not supported") + } + if d.TtlMin != 0 { + log.WithFields(log.Fields{ + "Topic": "Peer", + "Key": addr, + }).Warn("setting min ttl for active connection is not supported") + } + + raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port))) + if err != nil { + return nil, fmt.Errorf("invalid remote address: %s", err) + } + laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String()) + if err != nil { + return nil, fmt.Errorf("invalid local address: %s", err) + } + + dialer := net.Dialer{LocalAddr: laddr, Timeout: d.Timeout} + conn, err := dialer.Dial("tcp", raddr.String()) + if err != nil { + return nil, err + } + return conn.(*net.TCPConn), nil } diff --git a/server/util.go b/server/util.go index 951d712c..92f73504 100644 --- a/server/util.go +++ b/server/util.go @@ -16,7 +16,13 @@ package server import ( + "net" + "os" + "strings" + "syscall" + "github.com/eapache/channels" + "github.com/osrg/gobgp/packet/bgp" ) @@ -59,3 +65,28 @@ func decodeAdministrativeCommunication(data []byte) (string, []byte) { } return string(data[1 : communicationLen+1]), data[communicationLen+1:] } + +func extractFileAndFamilyFromTCPConn(conn *net.TCPConn) (*os.File, int, error) { + // Note #1: TCPConn.File() has the unexpected side-effect of putting + // the original socket into blocking mode. See Note #2. + fi, err := conn.File() + if err != nil { + return nil, 0, err + } + + // Note #2: Call net.FileConn() to put the original socket back into + // non-blocking mode. + fc, err := net.FileConn(fi) + if err != nil { + fi.Close() + return nil, 0, err + } + fc.Close() + + family := syscall.AF_INET + if strings.Contains(conn.RemoteAddr().String(), "[") { + family = syscall.AF_INET6 + } + + return fi, family, nil +} |