summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/fsm.go46
-rw-r--r--server/server.go4
-rw-r--r--server/sockopt.go60
-rw-r--r--server/sockopt_bsd.go115
-rw-r--r--server/sockopt_linux.go187
-rw-r--r--server/sockopt_openbsd.go114
-rw-r--r--server/util.go31
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
+}