Repository: metallb/metallb
Pull request #1127 (2021-12-15 14:40:53)

FRR: Support eBGP multi-hop peering 
Adding support for eBGP multi-hop peering in FRR is a must
as it is already supported by the native mode.

This is done by adding `ebgp-multihop` in FRR's config to a peer
that requires eBGP and is multi-hops away.

The problem with adding this without knowing if the ebgp peer is actually
multi-hops away breaks BFD, because BFD considers it multi-hop when
that parameter is set regardless if it actually is, and rejects the
peer in case it is not multi-hops away.

To solve that we introduce a new config parameter for a peer (ebgp-multihop)
that specifies if a given ebgp peer is multi-hops away or not, and setting
ebgp-multihop for that peer in FRR accordingly.

oribon: L2 fails should be solved by https://github.com/metallb/metallb/pull/1122
fedepaol: LGTM, thanks!

Patch:
diff --git a/go.sum b/go.sum
index 4f4e317a..17c19580 100644
--- a/go.sum
+++ b/go.sum
@@ -19,7 +19,6 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
@@ -59,11 +58,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20200415212048-7901bc822317/go.mod h1:DF8FZRxMHMGv/vP2lQP6h+dYzzjpuRn24VeRiYn3qjQ=
 github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
 github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
 github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
 github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
@@ -438,7 +434,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
 github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
 github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
 github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -525,8 +520,6 @@ github.com/mdlayher/raw v0.0.0-20190313224157-43dbcdd7739d/go.mod h1:r1fbeITl2xL
 github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
 github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
 github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
-github.com/metallb/metallb-operator v0.0.0-20211130125940-1fa9bd1b26e6 h1:SnlhuUWaybz321LlQrY+h3Hwfl3vdOtUniCMKttbHSk=
-github.com/metallb/metallb-operator v0.0.0-20211130125940-1fa9bd1b26e6/go.mod h1:OklSBaSeVcigNS4wtnHABJJlpSype7HZ7xg1gWDffh4=
 github.com/metallb/metallb-operator v0.0.0-20211214200449-6cf52753e656 h1:k4Iwe+5GyO2aVGDDLxgGA/s6oNqn9xjsdq6CUsKlGu4=
 github.com/metallb/metallb-operator v0.0.0-20211214200449-6cf52753e656/go.mod h1:OklSBaSeVcigNS4wtnHABJJlpSype7HZ7xg1gWDffh4=
 github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
@@ -540,7 +533,6 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCL
 github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
 github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -552,7 +544,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
 github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
 github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
diff --git a/internal/bgp/bgp.go b/internal/bgp/bgp.go
index 96e8abcb..794fa5f3 100644
--- a/internal/bgp/bgp.go
+++ b/internal/bgp/bgp.go
@@ -45,6 +45,6 @@ type Session interface {
 }
 
 type SessionManager interface {
-	NewSession(logger log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, hold, keepalive time.Duration, password, myNode, bfdProfile string) (Session, error)
+	NewSession(logger log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, hold, keepalive time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool) (Session, error)
 	SyncBFDProfiles(profiles map[string]*config.BFDProfile) error
 }
diff --git a/internal/bgp/frr/config.go b/internal/bgp/frr/config.go
index 32ee0cee..21f2e369 100644
--- a/internal/bgp/frr/config.go
+++ b/internal/bgp/frr/config.go
@@ -50,16 +50,19 @@ route-map {{$n.Addr}}-in deny 20
 {{- end }}
 {{- end }}
 
-{{range .Routers -}}
-router bgp {{.MyASN}}
+{{range $r := .Routers -}}
+router bgp {{$r.MyASN}}
   no bgp ebgp-requires-policy
   no bgp network import-check
   no bgp default ipv4-unicast
-{{ if .RouterId }}
-  bgp router-id {{.RouterId}}
+{{ if $r.RouterId }}
+  bgp router-id {{$r.RouterId}}
 {{- end }}
 {{range .Neighbors }}
   neighbor {{.Addr}} remote-as {{.ASN}}
+  {{- if .EBGPMultiHop }}
+  neighbor {{.Addr}} ebgp-multihop
+  {{- end }}
   {{ if .Port -}}
   neighbor {{.Addr}} port {{.Port}}
   {{- end }}
@@ -160,6 +163,7 @@ type neighborConfig struct {
 	Password       string
 	Advertisements []*advertisementConfig
 	BFDProfile     string
+	EBGPMultiHop   bool
 }
 
 type advertisementConfig struct {
diff --git a/internal/bgp/frr/frr.go b/internal/bgp/frr/frr.go
index 5b47e626..08e48a5d 100644
--- a/internal/bgp/frr/frr.go
+++ b/internal/bgp/frr/frr.go
@@ -37,6 +37,7 @@ type session struct {
 	password       string
 	advertised     []*bgp.Advertisement
 	bfdProfile     string
+	ebgpMultiHop   bool
 	sessionManager *sessionManager
 }
 
@@ -117,7 +118,7 @@ func (s *session) Close() error {
 //
 // The session will immediately try to connect and synchronize its
 // local state with the peer.
-func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string) (bgp.Session, error) {
+func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool) (bgp.Session, error) {
 	s := &session{
 		myASN:          myASN,
 		routerID:       routerID,
@@ -132,6 +133,7 @@ func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP,
 		advertised:     []*bgp.Advertisement{},
 		sessionManager: sm,
 		bfdProfile:     bfdProfile,
+		ebgpMultiHop:   ebgpMultiHop,
 	}
 
 	_ = sm.addSession(s)
@@ -240,6 +242,7 @@ func (sm *sessionManager) createConfig() (*frrConfig, error) {
 				Password:       s.password,
 				Advertisements: make([]*advertisementConfig, 0),
 				BFDProfile:     s.bfdProfile,
+				EBGPMultiHop:   s.ebgpMultiHop,
 			}
 			if s.srcAddr != nil {
 				neighbor.SrcAddr = s.srcAddr.String()
diff --git a/internal/bgp/native/native.go b/internal/bgp/native/native.go
index 4cdd3a62..044a7825 100644
--- a/internal/bgp/native/native.go
+++ b/internal/bgp/native/native.go
@@ -66,7 +66,7 @@ func NewSessionManager(l log.Logger) *sessionManager {
 //
 // The session will immediately try to connect and synchronize its
 // local state with the peer.
-func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string) (bgp.Session, error) {
+func (sm *sessionManager) NewSession(l log.Logger, addr string, srcAddr net.IP, myASN uint32, routerID net.IP, asn uint32, holdTime, keepaliveTime time.Duration, password, myNode, bfdProfile string, ebgpMultiHop bool) (bgp.Session, error) {
 	ret := &session{
 		addr:          addr,
 		srcAddr:       srcAddr,
diff --git a/internal/config/config.go b/internal/config/config.go
index f8c885a1..31991fdf 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -51,6 +51,7 @@ type peer struct {
 	NodeSelectors []nodeSelector `yaml:"node-selectors"`
 	Password      string         `yaml:"password"`
 	BFDProfile    string         `yaml:"bfd-profile"`
+	EBGPMultiHop  bool           `yaml:"ebgp-multihop"`
 }
 
 type nodeSelector struct {
@@ -135,6 +136,8 @@ type Peer struct {
 	Password string
 	// The optional BFD profile to be used for this BGP session
 	BFDProfile string
+	// Optional ebgp peer is multi-hops away.
+	EBGPMultiHop bool
 	// TODO: more BGP session settings
 }
 
@@ -335,6 +338,9 @@ func parsePeer(p peer) (*Peer, error) {
 	if p.ASN == 0 {
 		return nil, errors.New("missing peer ASN")
 	}
+	if p.ASN == p.MyASN && p.EBGPMultiHop {
+		return nil, errors.New("invalid ebgp-multihop parameter set for an ibgp peer")
+	}
 	ip := net.ParseIP(p.Addr)
 	if ip == nil {
 		return nil, fmt.Errorf("invalid peer IP %q", p.Addr)
@@ -403,6 +409,7 @@ func parsePeer(p peer) (*Peer, error) {
 		NodeSelectors: nodeSels,
 		Password:      password,
 		BFDProfile:    p.BFDProfile,
+		EBGPMultiHop:  p.EBGPMultiHop,
 	}, nil
 }
 
diff --git a/speaker/bgp_controller.go b/speaker/bgp_controller.go
index 7129c69f..30a8f637 100644
--- a/speaker/bgp_controller.go
+++ b/speaker/bgp_controller.go
@@ -206,7 +206,7 @@ func (c *bgpController) syncPeers(l log.Logger) error {
 			if p.cfg.RouterID != nil {
 				routerID = p.cfg.RouterID
 			}
-			s, err := c.sessionManager.NewSession(c.logger, net.JoinHostPort(p.cfg.Addr.String(), strconv.Itoa(int(p.cfg.Port))), p.cfg.SrcAddr, p.cfg.MyASN, routerID, p.cfg.ASN, p.cfg.HoldTime, p.cfg.KeepaliveTime, p.cfg.Password, c.myNode, p.cfg.BFDProfile)
+			s, err := c.sessionManager.NewSession(c.logger, net.JoinHostPort(p.cfg.Addr.String(), strconv.Itoa(int(p.cfg.Port))), p.cfg.SrcAddr, p.cfg.MyASN, routerID, p.cfg.ASN, p.cfg.HoldTime, p.cfg.KeepaliveTime, p.cfg.Password, c.myNode, p.cfg.BFDProfile, p.cfg.EBGPMultiHop)
 			if err != nil {
 				level.Error(l).Log("op", "syncPeers", "error", err, "peer", p.cfg.Addr, "msg", "failed to create BGP session")
 				errs++


Test patch:
diff --git a/e2etest/bgp_test.go b/e2etest/bgp_test.go
index b10b0743..9cfbcb71 100644
--- a/e2etest/bgp_test.go
+++ b/e2etest/bgp_test.go
@@ -27,6 +27,7 @@ import (
 	"time"
 
 	"go.universe.tf/metallb/e2etest/pkg/config"
+	"go.universe.tf/metallb/e2etest/pkg/container"
 	"go.universe.tf/metallb/e2etest/pkg/executor"
 	"go.universe.tf/metallb/e2etest/pkg/metrics"
 	"go.universe.tf/metallb/e2etest/pkg/routes"
@@ -53,8 +54,7 @@ import (
 )
 
 const (
-	frrIBGP         = "frr-iBGP"
-	frrEBGP         = "frr-eBGP"
+	multiHopNetwork = "multi-hop-net"
 	MetalLBASN      = 64512
 	ExternalASN     = 64513
 	baseRouterID    = "10.10.10.%d"
@@ -65,16 +65,34 @@ const (
 )
 
 var (
-	frrContainers []*frrcontainer.FRR
+	frrContainers  []*frrcontainer.FRR
+	multiHopRoutes map[string]container.NetworkSettings
 )
 
-func setupContainers(ipv4Addresses, ipv6Addresses []string) []*frrcontainer.FRR {
-
-	ibgpContainerConfig := frrcontainer.Config{
-		Name: frrIBGP,
+func setupContainers(ipv4Addresses, ipv6Addresses []string) ([]*frrcontainer.FRR, error) {
+	/*
+		We have 2 ways in which we setup the containers for the tests:
+		1 - The user requested the containers to use the 'host' network
+		so we spin up only one ibgp container.
+		2 - The user specified (or didn't at all) a container network that
+		is not 'host'. In that case he needs to supply 2 IPs for the containers.
+		Then we spin up a total of 4 containers:
+		  * ibgp container that uses the first IP, a single-hop away from our speakers (1st).
+		  * ebgp container that uses the second IP, a single-hop away from our speakers,
+		    and is connected to another containers network "multi-hop-net" (2nd).
+		  * two ibgp/ebgp containers connected to the "multi-hop-net", multi-hops away
+		    from our speakers (3rd,4th).
+		We then wire these networks by adding static routes to both the speaker nodes
+		containers (we're using kind) and the ibgp/ebgp containers connected to multi-hop-net,
+		using the 2nd container as a gateway.
+	*/
+
+	ibgpSingleHopContainerConfig := frrcontainer.Config{
+		Name: "ibgp-single-hop",
 		Neighbor: frrconfig.NeighborConfig{
 			ASN:      MetalLBASN,
 			Password: "ibgp-test",
+			MultiHop: false,
 		},
 		Router: frrconfig.RouterConfig{
 			ASN:      MetalLBASN,
@@ -85,43 +103,112 @@ func setupContainers(ipv4Addresses, ipv6Addresses []string) []*frrcontainer.FRR
 		HostIPv4: hostIPv4,
 		HostIPv6: hostIPv6,
 	}
-	ebgpContainerConfig := frrcontainer.Config{
-		Name: frrEBGP,
+	ibgpMultiHopContainerConfig := frrcontainer.Config{
+		Name: "ibgp-multi-hop",
+		Neighbor: frrconfig.NeighborConfig{
+			ASN:      MetalLBASN,
+			Password: "ibgp-test",
+			MultiHop: true,
+		},
+		Router: frrconfig.RouterConfig{
+			ASN:      MetalLBASN,
+			BGPPort:  180,
+			Password: "ibgp-test",
+		},
+		Network:  multiHopNetwork,
+		HostIPv4: hostIPv4,
+		HostIPv6: hostIPv6,
+	}
+	ebgpMultiHopContainerConfig := frrcontainer.Config{
+		Name: "ebgp-multi-hop",
 		Neighbor: frrconfig.NeighborConfig{
 			ASN:      MetalLBASN,
 			Password: "ebgp-test",
+			MultiHop: true,
 		},
 		Router: frrconfig.RouterConfig{
 			ASN:      ExternalASN,
 			BGPPort:  180,
 			Password: "ebgp-test",
 		},
-		Network:  containersNetwork,
+		Network:  multiHopNetwork,
 		HostIPv4: hostIPv4,
 		HostIPv6: hostIPv6,
 	}
+	ebgpSingleHopContainerConfig := frrcontainer.Config{
+		Name:    "ebgp-single-hop",
+		Network: containersNetwork,
+		Neighbor: frrconfig.NeighborConfig{
+			ASN:      MetalLBASN,
+			MultiHop: false,
+		},
+		Router: frrconfig.RouterConfig{
+			ASN:     ExternalASN,
+			BGPPort: 179,
+		},
+	}
 
 	var res []*frrcontainer.FRR
 	var err error
 	if containersNetwork == "host" {
-		res, err = createFRRContainers(ibgpContainerConfig)
+		res, err = createFRRContainers(ibgpSingleHopContainerConfig)
 	} else {
 		Expect(len(ipv4Addresses)).Should(BeNumerically(">=", 2))
 		Expect(len(ipv6Addresses)).Should(BeNumerically(">=", 2))
 
-		ibgpContainerConfig.IPv4Address = ipv4Addresses[0]
-		ibgpContainerConfig.IPv6Address = ipv6Addresses[0]
-		ebgpContainerConfig.IPv4Address = ipv4Addresses[1]
-		ebgpContainerConfig.IPv6Address = ipv6Addresses[1]
-		res, err = createFRRContainers(ibgpContainerConfig, ebgpContainerConfig)
+		ibgpSingleHopContainerConfig.IPv4Address = ipv4Addresses[0]
+		ibgpSingleHopContainerConfig.IPv6Address = ipv6Addresses[0]
+		ebgpSingleHopContainerConfig.IPv4Address = ipv4Addresses[1]
+		ebgpSingleHopContainerConfig.IPv6Address = ipv6Addresses[1]
+
+		var out string
+		out, err = executor.Host.Exec(executor.ContainerRuntime, "network", "create", multiHopNetwork, "--ipv6",
+			"--driver=bridge", "--subnet=172.30.0.0/16", "--subnet=fc00:f853:ccd:e798::/64")
+		if err != nil {
+			return res, errors.Wrapf(err, "failed to create %s: %s", multiHopNetwork, out)
+		}
+
+		res, err = createFRRContainers(ibgpSingleHopContainerConfig, ibgpMultiHopContainerConfig,
+			ebgpMultiHopContainerConfig, ebgpSingleHopContainerConfig)
+		if err != nil {
+			return res, err
+		}
+
+		out, err = executor.Host.Exec(executor.ContainerRuntime, "network", "connect",
+			multiHopNetwork, ebgpSingleHopContainerConfig.Name)
+		if err != nil {
+			return res, errors.Wrapf(err, "failed to connect %s to %s: %s", ebgpSingleHopContainerConfig.Name, multiHopNetwork, out)
+		}
+
+		multiHopRoutes, err = container.Networks(ebgpSingleHopContainerConfig.Name)
+		if err != nil {
+			return res, err
+		}
+
+		for _, c := range res {
+			if c.Network == multiHopNetwork {
+				err = container.AddMultiHop(c, c.Network, containersNetwork, multiHopRoutes)
+				if err != nil {
+					return res, err
+				}
+			}
+		}
 	}
-	framework.ExpectNoError(err)
-	return res
+	return res, err
 }
 
-func tearDownContainers(containers []*frrcontainer.FRR) {
+func tearDownContainers(containers []*frrcontainer.FRR) error {
 	err := stopFRRContainers(containers)
-	framework.ExpectNoError(err)
+	if err != nil {
+		return err
+	}
+
+	out, err := executor.Host.Exec(executor.ContainerRuntime, "network", "rm", multiHopNetwork)
+	if err != nil {
+		return errors.Wrapf(err, "failed to remove %s: %s", multiHopNetwork, out)
+	}
+
+	return nil
 }
 
 var _ = ginkgo.Describe("BGP", func() {
@@ -547,12 +634,12 @@ var _ = ginkgo.Describe("BGP", func() {
 					if err != nil {
 						return err
 					}
-					toCompare := config.BFDProfileWithDefaults(bfd)
 					err = frr.BFDPeersMatchNodes(allNodes.Items, bfdPeers, pairingFamily)
 					if err != nil {
 						return err
 					}
 					for _, peerConfig := range bfdPeers {
+						toCompare := config.BFDProfileWithDefaults(bfd, peerConfig.Multihop)
 						ginkgo.By(fmt.Sprintf("Checking bfd parameters on %s", peerConfig.Peer))
 						err := checkBFDConfigPropagated(toCompare, peerConfig)
 						if err != nil {
@@ -661,13 +748,25 @@ var _ = ginkgo.Describe("BGP", func() {
 			controllerPod := &pods.Items[0]
 			speakerPods := getSpeakerPods(cs)
 
-			var peerAddrs []string
+			var peers []struct {
+				addr     string
+				multihop bool
+			}
+
 			for _, c := range frrContainers {
 				address := c.Ipv4
 				if pairingFamily == "ipv6" {
 					address = c.Ipv6
 				}
-				peerAddrs = append(peerAddrs, address)
+
+				peers = append(peers, struct {
+					addr     string
+					multihop bool
+				}{
+					address,
+					c.NeighborConfig.MultiHop,
+				},
+				)
 			}
 
 			for _, speaker := range speakerPods {
@@ -679,44 +778,48 @@ var _ = ginkgo.Describe("BGP", func() {
 						return err
 					}
 
-					for _, addr := range peerAddrs {
-						err = validateGaugeValue(1, "metallb_bfd_session_up", map[string]string{"peer": addr}, speakerMetrics)
+					for _, peer := range peers {
+						err = validateGaugeValue(1, "metallb_bfd_session_up", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateCounterValue(1, "metallb_bfd_control_packet_input", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateCounterValue(1, "metallb_bfd_control_packet_input", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateCounterValue(1, "metallb_bfd_control_packet_output", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateCounterValue(1, "metallb_bfd_control_packet_output", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateGaugeValue(0, "metallb_bfd_session_down_events", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateGaugeValue(0, "metallb_bfd_session_down_events", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateCounterValue(1, "metallb_bfd_session_up_events", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateCounterValue(1, "metallb_bfd_session_up_events", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateCounterValue(1, "metallb_bfd_zebra_notifications", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateCounterValue(1, "metallb_bfd_zebra_notifications", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
 						if bfd.EchoMode != nil && *bfd.EchoMode {
-							err = validateCounterValue(1, "metallb_bfd_echo_packet_input", map[string]string{"peer": addr}, speakerMetrics)
+							echoVal := 1
+							if peer.multihop {
+								echoVal = 0
+							}
+							err = validateCounterValue(echoVal, "metallb_bfd_echo_packet_input", map[string]string{"peer": peer.addr}, speakerMetrics)
 							if err != nil {
 								return err
 							}
 
-							err = validateCounterValue(1, "metallb_bfd_echo_packet_output", map[string]string{"peer": addr}, speakerMetrics)
+							err = validateCounterValue(echoVal, "metallb_bfd_echo_packet_output", map[string]string{"peer": peer.addr}, speakerMetrics)
 							if err != nil {
 								return err
 							}
@@ -743,13 +846,13 @@ var _ = ginkgo.Describe("BGP", func() {
 						return err
 					}
 
-					for _, addr := range peerAddrs {
-						err = validateGaugeValue(0, "metallb_bfd_session_up", map[string]string{"peer": addr}, speakerMetrics)
+					for _, peer := range peers {
+						err = validateGaugeValue(0, "metallb_bfd_session_up", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
 
-						err = validateCounterValue(1, "metallb_bfd_session_down_events", map[string]string{"peer": addr}, speakerMetrics)
+						err = validateCounterValue(1, "metallb_bfd_session_down_events", map[string]string{"peer": peer.addr}, speakerMetrics)
 						if err != nil {
 							return err
 						}
@@ -1208,10 +1311,13 @@ func validateService(cs clientset.Interface, svc *corev1.Service, nodes []corev1
 				return err
 			}
 
-			advertised := routes.ForIP(ingressIP, c)
-			err = routes.MatchNodes(nodes, advertised, serviceIPFamily)
-			if err != nil {
-				return err
+			// The BGP routes will not match the nodes if static routes were added.
+			if !(c.Network == multiHopNetwork) {
+				advertised := routes.ForIP(ingressIP, c)
+				err = routes.MatchNodes(nodes, advertised, serviceIPFamily)
+				if err != nil {
+					return err
+				}
 			}
 
 			return nil
@@ -1277,6 +1383,7 @@ func createFRRContainers(c ...frrcontainer.Config) ([]*frrcontainer.FRR, error)
 			if err != nil {
 				return errors.Wrapf(err, "Failed to wait for daemons %v", toFind)
 			}
+
 			return nil
 		})
 	}
@@ -1307,15 +1414,20 @@ func peersForContainers(containers []*frrcontainer.FRR, ipFamily string) []confi
 		if i > 0 {
 			holdTime = fmt.Sprintf("%ds", i*180)
 		}
+		ebgpMultihop := false
+		if c.NeighborConfig.MultiHop && c.NeighborConfig.ASN != c.RouterConfig.ASN {
+			ebgpMultihop = true
+		}
 		for _, address := range addresses {
 			peers = append(peers, config.Peer{
-				Addr:     address,
-				ASN:      c.RouterConfig.ASN,
-				MyASN:    c.NeighborConfig.ASN,
-				Port:     c.RouterConfig.BGPPort,
-				RouterID: fmt.Sprintf(baseRouterID, i),
-				Password: c.RouterConfig.Password,
-				HoldTime: holdTime,
+				Addr:         address,
+				ASN:          c.RouterConfig.ASN,
+				MyASN:        c.NeighborConfig.ASN,
+				Port:         c.RouterConfig.BGPPort,
+				RouterID:     fmt.Sprintf(baseRouterID, i),
+				Password:     c.RouterConfig.Password,
+				HoldTime:     holdTime,
+				EBGPMultiHop: ebgpMultihop,
 			})
 		}
 	}
diff --git a/e2etest/e2etest_suite_test.go b/e2etest/e2etest_suite_test.go
index 17c89869..e3fccace 100644
--- a/e2etest/e2etest_suite_test.go
+++ b/e2etest/e2etest_suite_test.go
@@ -31,6 +31,8 @@ import (
 	"github.com/onsi/ginkgo/config"
 	"github.com/onsi/ginkgo/reporters"
 	"github.com/onsi/gomega"
+	"go.universe.tf/metallb/e2etest/pkg/container"
+	"go.universe.tf/metallb/e2etest/pkg/executor"
 	internalconfig "go.universe.tf/metallb/internal/config"
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/errors"
@@ -167,7 +169,24 @@ var _ = ginkgo.BeforeSuite(func() {
 	framework.ExpectNoError(err)
 	v4Addresses := strings.Split(ipv4ForContainers, ",")
 	v6Addresses := strings.Split(ipv6ForContainers, ",")
-	frrContainers = setupContainers(v4Addresses, v6Addresses)
+	frrContainers, err = setupContainers(v4Addresses, v6Addresses)
+	framework.ExpectNoError(err)
+
+	// Allow the speaker nodes to reach the multi-hop network containers.
+	if containersNetwork != "host" {
+		/*
+			When "host" network is not specified we assume that the tests
+			run on a kind cluster, where all the nodes are actually containers
+			on our pc. This allows us to create containerExecutors for the speakers
+			nodes, and edit their routes without any added privileges.
+		*/
+		speakerPods := getSpeakerPods(cs)
+		for _, pod := range speakerPods {
+			nodeExec := executor.ForContainer(pod.Spec.NodeName)
+			err = container.AddMultiHop(nodeExec, containersNetwork, multiHopNetwork, multiHopRoutes)
+			framework.ExpectNoError(err)
+		}
+	}
 })
 
 var _ = ginkgo.AfterSuite(func() {
@@ -177,5 +196,16 @@ var _ = ginkgo.AfterSuite(func() {
 	err = cs.CoreV1().ConfigMaps(testNameSpace).Delete(context.TODO(), configMapName, metav1.DeleteOptions{})
 	framework.ExpectNoError(err)
 
-	tearDownContainers(frrContainers)
+	err = tearDownContainers(frrContainers)
+	framework.ExpectNoError(err)
+
+	// Remove static routes from speaker nodes.
+	if containersNetwork != "host" {
+		speakerPods := getSpeakerPods(cs)
+		for _, pod := range speakerPods {
+			nodeExec := executor.ForContainer(pod.Spec.NodeName)
+			err = container.DeleteMultiHop(nodeExec, containersNetwork, multiHopNetwork, multiHopRoutes)
+			framework.ExpectNoError(err)
+		}
+	}
 })
diff --git a/e2etest/pkg/config/parse.go b/e2etest/pkg/config/parse.go
index ff27af8b..dee8ce2d 100644
--- a/e2etest/pkg/config/parse.go
+++ b/e2etest/pkg/config/parse.go
@@ -31,6 +31,7 @@ type Peer struct {
 	NodeSelectors []NodeSelector `yaml:"node-selectors,omitempty"`
 	Password      string         `yaml:"password,omitempty"`
 	BFDProfile    string         `yaml:"bfd-profile,omitempty"`
+	EBGPMultiHop  bool           `yaml:"ebgp-multihop,omitempty"`
 }
 
 type NodeSelector struct {
@@ -70,7 +71,7 @@ type BfdProfile struct {
 	MinimumTTL       *uint32 `yaml:"minimum-ttl,omitempty"`
 }
 
-func BFDProfileWithDefaults(profile BfdProfile) BfdProfile {
+func BFDProfileWithDefaults(profile BfdProfile, multiHop bool) BfdProfile {
 	res := BfdProfile{}
 	res.Name = profile.Name
 	res.ReceiveInterval = valueWithDefault(profile.ReceiveInterval, 300)
@@ -80,6 +81,12 @@ func BFDProfileWithDefaults(profile BfdProfile) BfdProfile {
 	res.MinimumTTL = valueWithDefault(profile.MinimumTTL, 254)
 	res.EchoMode = profile.EchoMode
 	res.PassiveMode = profile.PassiveMode
+
+	if multiHop {
+		res.EchoMode = boolPtr(false)
+		res.EchoInterval = uint32Ptr(50)
+	}
+
 	return res
 }
 
@@ -89,3 +96,11 @@ func valueWithDefault(v *uint32, def uint32) *uint32 {
 	}
 	return &def
 }
+
+func uint32Ptr(n uint32) *uint32 {
+	return &n
+}
+
+func boolPtr(b bool) *bool {
+	return &b
+}
diff --git a/e2etest/pkg/container/inspect.go b/e2etest/pkg/container/inspect.go
new file mode 100644
index 00000000..9294a753
--- /dev/null
+++ b/e2etest/pkg/container/inspect.go
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package container
+
+import (
+	"encoding/json"
+
+	"github.com/pkg/errors"
+	"go.universe.tf/metallb/e2etest/pkg/executor"
+)
+
+type NetworkSettings struct {
+	Gateway             string `json:"Gateway"`
+	IPAddress           string `json:"IPAddress"`
+	IPPrefixLen         int    `json:"IPPrefixLen"`
+	IPv6Gateway         string `json:"IPv6Gateway"`
+	GlobalIPv6Address   string `json:"GlobalIPv6Address"`
+	GlobalIPv6PrefixLen int    `json:"GlobalIPv6PrefixLen"`
+}
+
+func Networks(name string) (map[string]NetworkSettings, error) {
+	toParse := map[string]NetworkSettings{}
+	res, err := executor.Host.Exec(executor.ContainerRuntime, "inspect", "-f", "{{json .NetworkSettings.Networks}}", name)
+	if err != nil {
+		return toParse, errors.Wrapf(err, "Failed to inspect container %s %s", name, res)
+	}
+
+	err = json.Unmarshal([]byte(res), &toParse)
+	if err != nil {
+		return toParse, errors.Wrap(err, "failed to parse container inspect")
+	}
+
+	return toParse, nil
+}
diff --git a/e2etest/pkg/container/routes.go b/e2etest/pkg/container/routes.go
new file mode 100644
index 00000000..aa77f0b8
--- /dev/null
+++ b/e2etest/pkg/container/routes.go
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package container
+
+import (
+	"fmt"
+
+	"go.universe.tf/metallb/e2etest/pkg/executor"
+	"go.universe.tf/metallb/e2etest/pkg/routes"
+)
+
+// Adds the routes that enable communication between execnet and tonet using the ref routes.
+// The ref routes should come from the container that is connected to both execnet and tonet.
+func AddMultiHop(exec executor.Executor, execnet, tonet string, ref map[string]NetworkSettings) error {
+	localNetGW, ok := ref[execnet]
+	if !ok {
+		return fmt.Errorf("network %s not found in %v", execnet, ref)
+	}
+
+	externalNet, ok := ref[tonet]
+	if !ok {
+		return fmt.Errorf("network %s not found in %v", tonet, ref)
+	}
+
+	err := routes.Add(exec, fmt.Sprintf("%s/%d", externalNet.IPAddress, externalNet.IPPrefixLen), localNetGW.IPAddress)
+	if err != nil {
+		return err
+	}
+
+	err = routes.Add(exec, fmt.Sprintf("%s/%d", externalNet.GlobalIPv6Address, externalNet.GlobalIPv6PrefixLen), localNetGW.GlobalIPv6Address)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Deletes the routes that enable communication between execnet and tonet using the ref routes.
+func DeleteMultiHop(exec executor.Executor, execnet, tonet string, ref map[string]NetworkSettings) error {
+	localNetGW, ok := ref[execnet]
+	if !ok {
+		return fmt.Errorf("network %s not found in %v", execnet, ref)
+	}
+
+	externalNet, ok := ref[tonet]
+	if !ok {
+		return fmt.Errorf("network %s not found in %v", tonet, ref)
+	}
+
+	err := routes.Delete(exec, fmt.Sprintf("%s/%d", externalNet.IPAddress, externalNet.IPPrefixLen), localNetGW.IPAddress)
+	if err != nil {
+		return err
+	}
+
+	err = routes.Delete(exec, fmt.Sprintf("%s/%d", externalNet.GlobalIPv6Address, externalNet.GlobalIPv6PrefixLen), localNetGW.GlobalIPv6Address)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/e2etest/pkg/frr/config/config.go b/e2etest/pkg/frr/config/config.go
index 05a45359..6b46b08e 100644
--- a/e2etest/pkg/frr/config/config.go
+++ b/e2etest/pkg/frr/config/config.go
@@ -29,19 +29,25 @@ debug bgp neighbor
 debug zebra nht
 debug bgp nht
 debug bfd peer
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
 
 log file /tmp/frr.log debugging
 log timestamp precision 3
 
 route-map RMAP permit 10
 set ipv6 next-hop prefer-global
-router bgp {{.ASN}}
+{{$ROUTERASN:=.ASN}}
+router bgp {{$ROUTERASN}}
   bgp router-id {{.RouterID}}
   no bgp network import-check
   no bgp ebgp-requires-policy
   no bgp default ipv4-unicast
 {{range .Neighbors }}
   neighbor {{.Addr}} remote-as {{.ASN}}
+  {{- if and (ne .ASN $ROUTERASN) (.MultiHop) }}
+  neighbor {{.Addr}} ebgp-multihop
+  {{- end }}
   {{ if .Password -}}
   neighbor {{.Addr}} password {{.Password}}
   {{- end }}
@@ -92,6 +98,7 @@ type NeighborConfig struct {
 	IPFamily    string
 	BFDEnabled  bool
 	ToAdvertise string
+	MultiHop    bool
 }
 
 // Set the IP of each node in the cluster in the BGP router configuration.
diff --git a/e2etest/pkg/frr/container/container.go b/e2etest/pkg/frr/container/container.go
index 78bd0bdf..575f994c 100644
--- a/e2etest/pkg/frr/container/container.go
+++ b/e2etest/pkg/frr/container/container.go
@@ -35,6 +35,7 @@ type FRR struct {
 	RouterConfig   config.RouterConfig
 	Ipv4           string
 	Ipv6           string
+	Network        string
 }
 
 type Config struct {
@@ -69,6 +70,7 @@ func Start(cfg Config) (*FRR, error) {
 		configDir:      configDir,
 		NeighborConfig: cfg.Neighbor,
 		RouterConfig:   cfg.Router,
+		Network:        cfg.Network,
 	}
 
 	if cfg.Network == hostNetwork {
diff --git a/e2etest/pkg/routes/patch.go b/e2etest/pkg/routes/patch.go
new file mode 100644
index 00000000..9ccae5f4
--- /dev/null
+++ b/e2etest/pkg/routes/patch.go
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package routes
+
+import (
+	"net"
+
+	"github.com/pkg/errors"
+	"go.universe.tf/metallb/e2etest/pkg/executor"
+)
+
+// Add route to target via for the given Executor.
+func Add(exec executor.Executor, target, via string) error {
+	_, dst, err := net.ParseCIDR(target)
+	if err != nil {
+		return err
+	}
+
+	gw := net.ParseIP(via)
+
+	cmd := "ip"
+	args := []string{"route", "add", dst.String(), "via", gw.String()}
+	if dst.IP.To4() == nil {
+		args = []string{"-6", "route", "add", dst.String(), "via", gw.String()}
+	}
+	out, err := exec.Exec(cmd, args...)
+	if err != nil {
+		return errors.Wrapf(err, "Failed to add route %s %s %s", cmd, args, out)
+	}
+
+	return nil
+}
+
+// Delete route to target via for the given Executor.
+func Delete(exec executor.Executor, target, via string) error {
+	_, dst, err := net.ParseCIDR(target)
+	if err != nil {
+		return err
+	}
+
+	gw := net.ParseIP(via)
+
+	cmd := "ip"
+	args := []string{"route", "del", dst.String(), "via", gw.String()}
+	if dst.IP.To4() == nil {
+		args = []string{"-6", "route", "del", dst.String(), "via", gw.String()}
+	}
+	out, err := exec.Exec(cmd, args...)
+	if err != nil {
+		return errors.Wrapf(err, "Failed to delete route %s", out)
+	}
+
+	return nil
+}
diff --git a/internal/bgp/frr/frr_bfd_test.go b/internal/bgp/frr/frr_bfd_test.go
index 62cd2720..73ab2b41 100644
--- a/internal/bgp/frr/frr_bfd_test.go
+++ b/internal/bgp/frr/frr_bfd_test.go
@@ -119,7 +119,7 @@ func TestBFDWithSession(t *testing.T) {
 		t.Fatalf("Failed to sync bfd profiles %s", err)
 	}
 
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, 2*time.Second, "password", "hostname", "foo")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, 2*time.Second, "password", "hostname", "foo", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
diff --git a/internal/bgp/frr/frr_test.go b/internal/bgp/frr/frr_test.go
index 92b53346..632802f8 100644
--- a/internal/bgp/frr/frr_test.go
+++ b/internal/bgp/frr/frr_test.go
@@ -85,13 +85,43 @@ func testCheckConfigFile(t *testing.T) {
 	}
 }
 
-func TestSingleSession(t *testing.T) {
+func TestSingleEBGPSessionMultiHop(t *testing.T) {
 	testSetup(t)
 
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleEBGPSessionOneHop(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l, "127.0.0.2:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", false)
+	if err != nil {
+		t.Fatalf("Could not create session: %s", err)
+	}
+	defer session.Close()
+
+	testCheckConfigFile(t)
+}
+
+func TestSingleIBGPSession(t *testing.T) {
+	testSetup(t)
+
+	l := log.NewNopLogger()
+	sessionManager := NewSessionManager(l)
+	defer close(sessionManager.reloadConfig)
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 100, time.Second, time.Second, "password", "hostname", "", false)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -107,7 +137,7 @@ func TestSingleSessionClose(t *testing.T) {
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
 
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -121,12 +151,12 @@ func TestTwoSessions(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.3.3.254"), 300, net.ParseIP("10.3.3.254"), 400, time.Second, time.Second, "password", "hostname", "")
+	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.3.3.254"), 300, net.ParseIP("10.3.3.254"), 400, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -141,12 +171,12 @@ func TestTwoSessionsDuplicate(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session2, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -161,12 +191,12 @@ func TestTwoSessionsDuplicateRouter(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session1, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
 	defer session1.Close()
-	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 400, time.Second, time.Second, "password", "hostname", "")
+	session2, err := sessionManager.NewSession(l, "10.4.4.255:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 400, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -181,7 +211,7 @@ func TestSingleAdvertisement(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -217,7 +247,7 @@ func TestSingleAdvertisementNoRouterID(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, nil, 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, nil, 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -247,7 +277,7 @@ func TestSingleAdvertisementInvalidPrefix(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -274,7 +304,7 @@ func TestSingleAdvertisementInvalidNoPort(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err == nil {
 		session.Close()
 		t.Fatalf("Should not be able to create session")
@@ -290,7 +320,7 @@ func TestSingleAdvertisementInvalidNextHop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -319,7 +349,7 @@ func TestSingleAdvertisementStop(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -354,7 +384,7 @@ func TestSingleAdvertisementChange(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
@@ -399,7 +429,7 @@ func TestTwoAdvertisements(t *testing.T) {
 	l := log.NewNopLogger()
 	sessionManager := NewSessionManager(l)
 	defer close(sessionManager.reloadConfig)
-	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "")
+	session, err := sessionManager.NewSession(l, "10.2.2.254:179", net.ParseIP("10.1.1.254"), 100, net.ParseIP("10.1.1.254"), 200, time.Second, time.Second, "password", "hostname", "", true)
 	if err != nil {
 		t.Fatalf("Could not create session: %s", err)
 	}
diff --git a/internal/bgp/frr/testdata/TestBFDWithSession.golden b/internal/bgp/frr/testdata/TestBFDWithSession.golden
index 73f4f0a3..e46fbba3 100644
--- a/internal/bgp/frr/testdata/TestBFDWithSession.golden
+++ b/internal/bgp/frr/testdata/TestBFDWithSession.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 2 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
index b30bd237..46cd13da 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
@@ -18,6 +18,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
index 26c5b3b8..e5bf11e3 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementInvalidPrefix.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementInvalidPrefix.golden
index 1668fdf1..3e8d426b 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementInvalidPrefix.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementInvalidPrefix.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
index 854d041d..38ff6a21 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
@@ -13,6 +13,7 @@ router bgp 100
 
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
index 1668fdf1..3e8d426b 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleSession.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
similarity index 95%
rename from internal/bgp/frr/testdata/TestSingleSession.golden
rename to internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
index 1668fdf1..3e8d426b 100644
--- a/internal/bgp/frr/testdata/TestSingleSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
new file mode 100644
index 00000000..a9661038
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
@@ -0,0 +1,29 @@
+
+log file /etc/frr/frr.log informational
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 127.0.0.2-in deny 20
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+
+  neighbor 127.0.0.2 remote-as 200
+  neighbor 127.0.0.2 port 179
+  neighbor 127.0.0.2 timers 1 1
+  neighbor 127.0.0.2 password password
+  neighbor 127.0.0.2 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 127.0.0.2 activate
+    neighbor 127.0.0.2 route-map 127.0.0.2-in in
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 127.0.0.2 activate
+    neighbor 127.0.0.2 route-map 127.0.0.2-in in
+  exit-address-family
diff --git a/internal/bgp/frr/testdata/TestSingleIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
new file mode 100644
index 00000000..0d0a4f86
--- /dev/null
+++ b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
@@ -0,0 +1,29 @@
+
+log file /etc/frr/frr.log informational
+log timestamp precision 3
+hostname dummyhostname
+ip nht resolve-via-default
+ipv6 nht resolve-via-default
+route-map 10.2.2.254-in deny 20
+
+router bgp 100
+  no bgp ebgp-requires-policy
+  no bgp network import-check
+  no bgp default ipv4-unicast
+
+  bgp router-id 10.1.1.254
+
+  neighbor 10.2.2.254 remote-as 100
+  neighbor 10.2.2.254 port 179
+  neighbor 10.2.2.254 timers 1 1
+  neighbor 10.2.2.254 password password
+  neighbor 10.2.2.254 update-source 10.1.1.254
+
+  address-family ipv4 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+  exit-address-family
+  address-family ipv6 unicast
+    neighbor 10.2.2.254 activate
+    neighbor 10.2.2.254 route-map 10.2.2.254-in in
+  exit-address-family
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
index b7c232a5..b8f5c6b8 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
@@ -16,6 +16,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestTwoSessions.golden b/internal/bgp/frr/testdata/TestTwoSessions.golden
index 2265aa8b..65e07c4b 100644
--- a/internal/bgp/frr/testdata/TestTwoSessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessions.golden
@@ -15,6 +15,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
@@ -36,6 +37,7 @@ router bgp 300
   bgp router-id 10.3.3.254
 
   neighbor 10.4.4.255 remote-as 400
+  neighbor 10.4.4.255 ebgp-multihop
   neighbor 10.4.4.255 port 179
   neighbor 10.4.4.255 timers 1 1
   neighbor 10.4.4.255 password password
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
index 1668fdf1..3e8d426b 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
@@ -14,6 +14,7 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
index 75f93873..b6951cc2 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
@@ -15,11 +15,13 @@ router bgp 100
   bgp router-id 10.1.1.254
 
   neighbor 10.2.2.254 remote-as 200
+  neighbor 10.2.2.254 ebgp-multihop
   neighbor 10.2.2.254 port 179
   neighbor 10.2.2.254 timers 1 1
   neighbor 10.2.2.254 password password
   neighbor 10.2.2.254 update-source 10.1.1.254
   neighbor 10.4.4.255 remote-as 400
+  neighbor 10.4.4.255 ebgp-multihop
   neighbor 10.4.4.255 port 179
   neighbor 10.4.4.255 timers 1 1
   neighbor 10.4.4.255 password password
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index fcbcba3a..4ed9c736 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -58,9 +58,11 @@ peers:
   hold-time: 180s
   router-id: 10.20.30.40
   source-address: 10.20.30.40
+  ebgp-multihop: true
 - my-asn: 100
   peer-asn: 200
   peer-address: 2.3.4.5
+  ebgp-multihop: false
   node-selectors:
   - match-labels:
       foo: bar
@@ -110,6 +112,7 @@ address-pools:
 						KeepaliveTime: 60 * time.Second,
 						RouterID:      net.ParseIP("10.20.30.40"),
 						NodeSelectors: []labels.Selector{labels.Everything()},
+						EBGPMultiHop:  true,
 					},
 					{
 						MyASN:         100,
@@ -119,6 +122,7 @@ address-pools:
 						HoldTime:      90 * time.Second,
 						KeepaliveTime: 30 * time.Second,
 						NodeSelectors: []labels.Selector{selector("bar in (quux),foo=bar")},
+						EBGPMultiHop:  false,
 					},
 				},
 				Pools: map[string]*Pool{
@@ -202,6 +206,7 @@ peers:
 						HoldTime:      90 * time.Second,
 						KeepaliveTime: 30 * time.Second,
 						NodeSelectors: []labels.Selector{labels.Everything()},
+						EBGPMultiHop:  false,
 					},
 				},
 				Pools:       map[string]*Pool{},
@@ -237,6 +242,17 @@ peers:
 `,
 		},
 
+		{
+			desc: "invalid ebgp-multihop",
+			raw: `
+peers:
+- my-asn: 42
+  peer-asn: 42
+  peer-address: 1.2.3.4
+  ebgp-multihop: true
+`,
+		},
+
 		{
 			desc: "invalid hold time (wrong format)",
 			raw: `
diff --git a/speaker/bgp_controller_test.go b/speaker/bgp_controller_test.go
index 0b8e46ab..7f7cc6c9 100644
--- a/speaker/bgp_controller_test.go
+++ b/speaker/bgp_controller_test.go
@@ -110,7 +110,7 @@ type fakeBGPSessionManager struct {
 	gotAds map[string][]*bgp.Advertisement
 }
 
-func (f *fakeBGPSessionManager) NewSession(_ log.Logger, addr string, _ net.IP, _ uint32, _ net.IP, _ uint32, _ time.Duration, _ time.Duration, _, _, _ string) (bgp.Session, error) {
+func (f *fakeBGPSessionManager) NewSession(_ log.Logger, addr string, _ net.IP, _ uint32, _ net.IP, _ uint32, _ time.Duration, _ time.Duration, _, _, _ string, _ bool) (bgp.Session, error) {
 	f.Lock()
 	defer f.Unlock()