From e7534de3d222742f839c3cf137bbfc40ddfafc0b Mon Sep 17 00:00:00 2001 From: Maxime Peim Date: Thu, 6 Jul 2023 15:03:56 +0000 Subject: [PATCH] use TCP MSS configuration --- internal/pkg/config/util.go | 2 ++ pkg/server/fsm.go | 25 ++++++++++++++++++++++--- pkg/server/grpc_server.go | 2 ++ pkg/server/sockopt.go | 12 +++++++++++- pkg/server/sockopt_bsd.go | 9 +++++++++ pkg/server/sockopt_darwin.go | 9 +++++++++ pkg/server/sockopt_linux.go | 25 ++++++++++++++++++++++++- pkg/server/sockopt_openbsd.go | 24 +++++++++++++++++++++++- pkg/server/sockopt_stub.go | 4 ++++ pkg/server/sockopt_windows.go | 21 ++++++++++++++++++++- pkg/server/util.go | 6 ++++++ 11 files changed, 132 insertions(+), 7 deletions(-) diff --git a/internal/pkg/config/util.go b/internal/pkg/config/util.go index 447e8dd1..5736a4c1 100644 --- a/internal/pkg/config/util.go +++ b/internal/pkg/config/util.go @@ -563,6 +563,7 @@ func NewPeerFromConfigStruct(pconf *Neighbor) *api.Peer { LocalAddress: localAddress, PassiveMode: pconf.Transport.Config.PassiveMode, BindInterface: pconf.Transport.Config.BindInterface, + TcpMss: uint32(pconf.Transport.Config.TcpMss), }, AfiSafis: afiSafis, } @@ -640,6 +641,7 @@ func NewPeerGroupFromConfigStruct(pconf *PeerGroup) *api.PeerGroup { RemotePort: uint32(pconf.Transport.Config.RemotePort), LocalAddress: pconf.Transport.Config.LocalAddress, PassiveMode: pconf.Transport.Config.PassiveMode, + TcpMss: uint32(pconf.Transport.Config.TcpMss), }, AfiSafis: afiSafis, } diff --git a/pkg/server/fsm.go b/pkg/server/fsm.go index f3c3b71c..ed64d26d 100644 --- a/pkg/server/fsm.go +++ b/pkg/server/fsm.go @@ -494,7 +494,7 @@ func (h *fsmHandler) connectLoop(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() fsm := h.fsm - retry, addr, port, password, ttl, ttlMin, localAddress, localPort, bindInterface := func() (int, string, int, string, uint8, uint8, string, int, string) { + retry, addr, port, password, ttl, ttlMin, mss, localAddress, localPort, bindInterface := func() (int, string, int, string, uint8, uint8, uint16, string, int, string) { fsm.lock.RLock() defer fsm.lock.RUnlock() @@ -521,7 +521,7 @@ func (h *fsmHandler) connectLoop(ctx context.Context, wg *sync.WaitGroup) { ttl = fsm.pConf.EbgpMultihop.Config.MultihopTtl } } - return tick, addr, port, password, ttl, ttlMin, fsm.pConf.Transport.Config.LocalAddress, int(fsm.pConf.Transport.Config.LocalPort), fsm.pConf.Transport.Config.BindInterface + return tick, addr, port, password, ttl, ttlMin, fsm.pConf.Transport.Config.TcpMss, fsm.pConf.Transport.Config.LocalAddress, int(fsm.pConf.Transport.Config.LocalPort), fsm.pConf.Transport.Config.BindInterface }() tick := minConnectRetryInterval @@ -556,7 +556,7 @@ func (h *fsmHandler) connectLoop(ctx context.Context, wg *sync.WaitGroup) { LocalAddr: laddr, Timeout: time.Duration(tick-1) * time.Second, Control: func(network, address string, c syscall.RawConn) error { - return dialerControl(fsm.logger, network, address, c, ttl, ttlMin, password, bindInterface) + return dialerControl(fsm.logger, network, address, c, ttl, ttlMin, mss, password, bindInterface) }, } @@ -634,6 +634,14 @@ func (h *fsmHandler) active(ctx context.Context) (bgp.FSMState, *fsmStateReason) "State": fsm.state.String(), "Error": err}) } + if err := setPeerConnMSS(fsm); err != nil { + fsm.logger.Warn("cannot set MSS for peer", + log.Fields{ + "Topic": "Peer", + "Key": fsm.pConf.Config.NeighborAddress, + "State": fsm.state.String(), + "Error": err}) + } fsm.lock.RUnlock() // we don't implement delayed open timer so move to opensent right // away. @@ -705,6 +713,17 @@ func setPeerConnTTL(fsm *fsm) error { return nil } +func setPeerConnMSS(fsm *fsm) error { + mss := fsm.pConf.Transport.Config.TcpMss + if mss == 0 { + return nil + } + if err := setTCPMSSSockopt(fsm.conn.(*net.TCPConn), mss); err != nil { + return fmt.Errorf("failed to set MSS %d: %w", mss, err) + } + return nil +} + func capAddPathFromConfig(pConf *config.Neighbor) bgp.ParameterCapabilityInterface { tuples := make([]*bgp.CapAddPathTuple, 0, len(pConf.AfiSafis)) for _, af := range pConf.AfiSafis { diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index ee52f414..ad05d8fe 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -728,6 +728,7 @@ func newNeighborFromAPIStruct(a *api.Peer) (*config.Neighbor, error) { pconf.Transport.Config.RemotePort = uint16(a.Transport.RemotePort) pconf.Transport.Config.LocalPort = uint16(a.Transport.LocalPort) pconf.Transport.Config.BindInterface = a.Transport.BindInterface + pconf.Transport.Config.TcpMss = uint16(a.Transport.TcpMss) } if a.EbgpMultihop != nil { pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled @@ -836,6 +837,7 @@ func newPeerGroupFromAPIStruct(a *api.PeerGroup) (*config.PeerGroup, error) { pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode pconf.Transport.Config.RemotePort = uint16(a.Transport.RemotePort) + pconf.Transport.Config.TcpMss = uint16(a.Transport.TcpMss) } if a.EbgpMultihop != nil { pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled diff --git a/pkg/server/sockopt.go b/pkg/server/sockopt.go index 38eed1cf..df864f97 100644 --- a/pkg/server/sockopt.go +++ b/pkg/server/sockopt.go @@ -41,7 +41,11 @@ func setBindToDevSockopt(sc syscall.RawConn, device string) error { return fmt.Errorf("binding connection to a device is not supported") } -func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, password string, bindInterface string) error { +func setTCPMSSSockopt(conn *net.TCPConn, mss uint16) error { + return setTcpMSSSockopt(conn, mss) +} + +func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, mss uint16, password string, bindInterface string) error { if password != "" { logger.Warn("setting md5 for active connection is not supported", log.Fields{ @@ -60,5 +64,11 @@ func dialerControl(logger log.Logger, network, address string, c syscall.RawConn "Topic": "Peer", "Key": address}) } + if mss != 0 { + logger.Warn("setting MSS for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } return nil } diff --git a/pkg/server/sockopt_bsd.go b/pkg/server/sockopt_bsd.go index 58411cc6..7a0504e6 100644 --- a/pkg/server/sockopt_bsd.go +++ b/pkg/server/sockopt_bsd.go @@ -59,3 +59,12 @@ func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { } return setsockOptInt(sc, level, name, ttl) } + +func setTcpMSSSockopt(conn *net.TCPConn, mss uint16) error { + family := extractFamilyFromTCPConn(conn) + sc, err := conn.SyscallConn() + if err != nil { + return err + } + return setsockoptTcpMss(sc, family, mss) +} diff --git a/pkg/server/sockopt_darwin.go b/pkg/server/sockopt_darwin.go index 2322d283..88e191b4 100644 --- a/pkg/server/sockopt_darwin.go +++ b/pkg/server/sockopt_darwin.go @@ -43,3 +43,12 @@ func setTcpTTLSockopt(conn *net.TCPConn, ttl int) error { func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { return fmt.Errorf("setting min ttl is not supported") } + +func setTcpMSSSockopt(conn *net.TCPConn, mss uint16) error { + family := extractFamilyFromTCPConn(conn) + sc, err := conn.SyscallConn() + if err != nil { + return err + } + return setsockoptTcpMss(sc, family, mss) +} diff --git a/pkg/server/sockopt_linux.go b/pkg/server/sockopt_linux.go index ffa67b97..6891261b 100644 --- a/pkg/server/sockopt_linux.go +++ b/pkg/server/sockopt_linux.go @@ -92,7 +92,16 @@ func setTCPMinTTLSockopt(conn *net.TCPConn, ttl int) error { return setsockOptInt(sc, level, name, ttl) } -func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, password string, bindInterface string) error { +func setTCPMSSSockopt(conn *net.TCPConn, mss uint16) error { + family := extractFamilyFromTCPConn(conn) + sc, err := conn.SyscallConn() + if err != nil { + return err + } + return setsockoptTcpMss(sc, family, mss) +} + +func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, mss uint16, password string, bindInterface string) error { family := syscall.AF_INET raddr, _ := net.ResolveTCPAddr("tcp", address) if raddr.IP.To4() == nil { @@ -146,6 +155,20 @@ func dialerControl(logger log.Logger, network, address string, c syscall.RawConn return sockerr } } + + if mss != 0 { + if err := c.Control(func(fd uintptr) { + level := syscall.IPPROTO_TCP + name := syscall.TCP_MAXSEG + sockerr = os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fd), level, name, int(mss))) + }); err != nil { + return err + } + if sockerr != nil { + return sockerr + } + } + if bindInterface != "" { if err := setBindToDevSockopt(c, bindInterface); err != nil { return err diff --git a/pkg/server/sockopt_openbsd.go b/pkg/server/sockopt_openbsd.go index d11959ec..6f34886d 100644 --- a/pkg/server/sockopt_openbsd.go +++ b/pkg/server/sockopt_openbsd.go @@ -384,11 +384,20 @@ func setTCPMinTTLSockopt(conn *net.TCPConn, ttl int) error { return setsockOptInt(sc, level, name, ttl) } +func setTCPMSSSockopt(conn *net.TCPConn, mss uint16) error { + family := extractFamilyFromTCPConn(conn) + sc, err := conn.SyscallConn() + if err != nil { + return err + } + return setsockoptTcpMss(sc, family, mss) +} + func setBindToDevSockopt(sc syscall.RawConn, device string) error { return fmt.Errorf("binding connection to a device is not supported") } -func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, password string, bindInterface string) error { +func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, minTtl uint8, mss uint16, password string, bindInterface string) error { if password != "" { logger.Warn("setting md5 for active connection is not supported", log.Fields{ @@ -407,5 +416,18 @@ func dialerControl(logger log.Logger, network, address string, c syscall.RawConn "Topic": "Peer", "Key": address}) } + var sockerr error + if mss != 0 { + if err := c.Control(func(fd uintptr) { + level := syscall.IPPROTO_TCP + name := syscall.TCP_MAXSEG + sockerr = os.NewSyscallError("setsockopt", syscall.SetsockoptInt(int(fd), level, name, int(mss))) + }); err != nil { + return err + } + if sockerr != nil { + return sockerr + } + } return nil } diff --git a/pkg/server/sockopt_stub.go b/pkg/server/sockopt_stub.go index e9c7ee25..09d17396 100644 --- a/pkg/server/sockopt_stub.go +++ b/pkg/server/sockopt_stub.go @@ -33,3 +33,7 @@ func setTcpTTLSockopt(conn *net.TCPConn, ttl int) error { func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error { return fmt.Errorf("setting min ttl is not supported") } + +func setTcpMSSSockopt(conn *net.TCPConn, mss uint16) error { + return fmt.Errorf("setting min ttl is not supported") +} diff --git a/pkg/server/sockopt_windows.go b/pkg/server/sockopt_windows.go index f803ed73..f412c1a6 100644 --- a/pkg/server/sockopt_windows.go +++ b/pkg/server/sockopt_windows.go @@ -29,6 +29,7 @@ const ( tcpMD5SIG = 14 // TCP MD5 Signature (RFC2385) ipv6MinHopCount = 73 // Generalized TTL Security Mechanism (RFC5082) IP_MINTTL = 0x15 // pulled from https://golang.org/pkg/syscall/?GOOS=linux#IP_MINTTL + TCP_MAXSEG = 0x2 // pulled from https://pkg.go.dev/syscall?GOOS=linux#TCP_MAXSEG ) func setTCPMD5SigSockopt(l *net.TCPListener, address string, key string) error { @@ -63,7 +64,19 @@ func setTCPMinTTLSockopt(conn *net.TCPConn, ttl int) error { return setsockOptInt(sc, level, name, ttl) } -func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, ttlMin uint8, password string, bindInterface string) error { +func setTCPMSSSockopt(conn *net.TCPConn, mss uint16) error { + // TCP_MAXSEG syscall option exists only from Windows 10 + // https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockopt + sc, err := conn.SyscallConn() + if err != nil { + return err + } + level := syscall.IPPROTO_TCP + name := TCP_MAXSEG + return setsockOptInt(sc, level, name, int(mss)) +} + +func dialerControl(logger log.Logger, network, address string, c syscall.RawConn, ttl, ttlMin uint8, mss uint16, password string, bindInterface string) error { if password != "" { logger.Warn("setting md5 for active connection is not supported", log.Fields{ @@ -82,5 +95,11 @@ func dialerControl(logger log.Logger, network, address string, c syscall.RawConn "Topic": "Peer", "Key": address}) } + if mss != 0 { + logger.Warn("setting MSS for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } return nil } diff --git a/pkg/server/util.go b/pkg/server/util.go index d0906b2b..9b0b7181 100644 --- a/pkg/server/util.go +++ b/pkg/server/util.go @@ -108,3 +108,9 @@ func setsockoptIpTtl(sc syscall.RawConn, family int, value int) error { } return setsockOptInt(sc, level, name, value) } + +func setsockoptTcpMss(sc syscall.RawConn, family int, value uint16) error { + level := syscall.IPPROTO_TCP + name := syscall.TCP_MAXSEG + return setsockOptInt(sc, level, name, int(value)) +}