diff --git a/internal/pkg/config/util.go b/internal/pkg/config/util.go index 447e8dd15..5736a4c1c 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 f3c3b71ca..ed64d26d5 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 ee52f414e..ad05d8fe4 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 38eed1cfb..307769b75 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(sc, 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 58411cc64..1b0ca3cb8 100644 --- a/pkg/server/sockopt_bsd.go +++ b/pkg/server/sockopt_bsd.go @@ -59,3 +59,46 @@ 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 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{ + "Topic": "Peer", + "Key": address}) + } + if ttl != 0 { + logger.Warn("setting ttl for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } + if minTtl != 0 { + logger.Warn("setting min ttl for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } + 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_darwin.go b/pkg/server/sockopt_darwin.go index 2322d2832..27a356050 100644 --- a/pkg/server/sockopt_darwin.go +++ b/pkg/server/sockopt_darwin.go @@ -43,3 +43,46 @@ 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) +} + +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{ + "Topic": "Peer", + "Key": address}) + } + if ttl != 0 { + logger.Warn("setting ttl for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } + if minTtl != 0 { + logger.Warn("setting min ttl for active connection is not supported", + log.Fields{ + "Topic": "Peer", + "Key": address}) + } + 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_linux.go b/pkg/server/sockopt_linux.go index ffa67b97e..6891261bc 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 d11959ec0..2eb4fb873 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,17 @@ func dialerControl(logger log.Logger, network, address string, c syscall.RawConn "Topic": "Peer", "Key": address}) } + 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 e9c7ee251..ca749f250 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 f803ed736..9cd739090 100644 --- a/pkg/server/sockopt_windows.go +++ b/pkg/server/sockopt_windows.go @@ -63,7 +63,13 @@ 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 + return fmt.Errorf("setting MSS is not supported") +} + +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 +88,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 d0906b2b1..9b0b71818 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)) +}