Skip to content

Commit 731ffd2

Browse files
committed
Bgp: fix: make iBGP work over a multi-hop IGP with IPv6
Running BGP over an interior gateway protocol (the usual transit-AS setup) did not work for IPv6 when an iBGP peer or a route's next hop was reachable only via several IGP hops (e.g. OSPFv3). Four related problems, each fixed so the directly-connected / IPv4 paths are unchanged: - iBGP session start threw a fatal error ("No configuration interface for peer address") when the IGP had not yet installed a route to the peer. Defer the start and retry until the peer becomes reachable (BgpFsm Idle::ManualStart, BgpSession::scheduleStartRetry/isPeerReachable, BgpRouter::findNextSession). - The iBGP path that adopts an existing (e.g. IGP) route into BGP cast the route unconditionally to Ipv4Route, aborting for IPv6. Use the generic IRoute; createBgpRoutingTableEntry() already builds the right v4/v6 entry. - A locally-originated route is built from a connected route and has no next hop, so it was advertised over IPv6 iBGP with an unspecified MP_REACH_NLRI next hop. Advertise our own on-link address instead (RFC 4271: a router is the next hop for the networks it originates). IPv6 path only. - An installed iBGP route used the (possibly multi-hop) BGP next hop directly as its gateway, so IPv6 neighbour discovery looked for it on the wrong link and failed. Resolve the next hop recursively through the IGP and install the immediate on-link next hop; a directly-connected next hop is kept as-is. The bgpv4 and tutorials/bgp fingerprints are unchanged (these paths only trigger for not-yet-reachable / multi-hop IPv6 next hops).
1 parent 7cf0a4f commit 731ffd2

4 files changed

Lines changed: 47 additions & 3 deletions

File tree

src/inet/routing/bgpv4/BgpFsm.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ void Idle::ManualStart()
3333
session._connectRetryCounter = 0;
3434
// - starts the ConnectRetryTimer with the initial value,
3535
session.stopConnectRetryTimer();
36+
// For an internal (iBGP) session, the peer may not be reachable yet if the IGP
37+
// (e.g. OSPFv3) has not converged. Defer the connection until a route to the peer
38+
// exists, instead of failing in openTcpConnectionToPeer().
39+
if (session.getType() == IGP && !session.isPeerReachable()) {
40+
session.scheduleStartRetry();
41+
return;
42+
}
3643
// - listens for a connection that may be initiated by the remote BGP peer,
3744
session.listenConnectionFromPeer();
3845
// - initiates a TCP connection to the other BGP peer and,

src/inet/routing/bgpv4/BgpRouter.cc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,9 @@ BgpProcessResult BgpRouter::decisionProcess(const BgpUpdateMessage& msg, BgpRout
687687
if (rt->getRoute(indexIp)->getSourceType() != IRoute::BGP) {
688688
// and the Update msg is coming from IGP session
689689
if (_bgpSessions[sessionIndex]->getType() == IGP) {
690-
Ipv4Route *oldEntry = check_and_cast<Ipv4Route *>(rt->getRoute(indexIp));
690+
// works for both IPv4 and IPv6: createBgpRoutingTableEntry() takes the
691+
// generic IRoute and builds the right (v4/v6) BGP entry
692+
IRoute *oldEntry = rt->getRoute(indexIp);
691693
BgpRouteInfo *bgpEntry = createBgpRoutingTableEntry(oldEntry);
692694
bgpEntry->addAS(myAsId);
693695
bgpEntry->setPathType(IGP);
@@ -709,6 +711,14 @@ BgpProcessResult BgpRouter::decisionProcess(const BgpUpdateMessage& msg, BgpRout
709711
if (_bgpSessions[sessionIndex]->getType() == IGP) {
710712
// if the next hop is reachable
711713
if (isReachable(entry->getNextHopAsGeneric())) {
714+
// Resolve the (i)BGP next hop recursively through the IGP. An installed route
715+
// must point at an on-link next hop, but an iBGP next hop can be several IGP
716+
// hops away (e.g. behind an OSPF/OSPFv3 path); in that case replace it with the
717+
// IGP's immediate next hop. A directly-connected next hop resolves to
718+
// "unspecified" here and is kept as-is (so directly-connected iBGP is unchanged).
719+
L3Address onLinkNextHop = rt->getNextHopForDestination(entry->getNextHopAsGeneric());
720+
if (!onLinkNextHop.isUnspecified())
721+
entry->setNextHop(onLinkNextHop);
712722
entry->setInterface(_bgpSessions[sessionIndex]->getLinkIntf());
713723
rt->addRoute(entry->asRoute());
714724
}
@@ -844,6 +854,12 @@ void BgpRouter::updateSendProcess(BgpProcessResult type, SessionId sessionIndex,
844854
: entry->getNextHopAsGeneric();
845855

846856
if (isIpv6()) {
857+
// a locally-originated route is built from a connected route and carries no
858+
// learned next hop; advertise our own address on the link so that iBGP peers
859+
// can resolve it via the IGP (RFC 4271: a router uses itself as the next hop
860+
// for the networks it originates)
861+
if (nextHop.isUnspecified())
862+
nextHop = getInterfaceAddress((elem).second->getLinkIntf());
847863
// RFC 4760: carry the IPv6 next hop + reachability in MP_REACH_NLRI; ORIGIN stays
848864
// a normal attribute. The legacy NEXT_HOP attribute and NLRI field are not used.
849865
auto originAttr = new BgpUpdatePathAttributesOrigin;
@@ -983,8 +999,12 @@ SessionId BgpRouter::findNextSession(BgpSessionType type, bool startSession)
983999
// note: if the internal peer is not directly-connected to us, then we should know how to reach it.
9841000
// this is done with the help of an intra-AS routing protocol (RIP, OSPF, EIGRP).
9851001
NetworkInterface *linkIntf = rt->getOutputInterfaceForDestination(_bgpSessions[sessionId]->getPeerAddr());
986-
if (linkIntf == nullptr)
987-
throw cRuntimeError("No configuration interface for peer address: %s", _bgpSessions[sessionId]->getPeerAddr().str().c_str());
1002+
if (linkIntf == nullptr) {
1003+
// The IGP has not installed a route to this internal peer yet (it may still be
1004+
// converging). Defer the session start and retry, instead of failing.
1005+
_bgpSessions[sessionId]->scheduleStartRetry();
1006+
return sessionId;
1007+
}
9881008

9891009
_bgpSessions[sessionId]->setlinkIntf(linkIntf);
9901010
_bgpSessions[sessionId]->startConnection();

src/inet/routing/bgpv4/BgpSession.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,18 @@ void BgpSession::scheduleReconnect()
9797
_ptrStartEvent->setContextPointer(this);
9898
}
9999

100+
void BgpSession::scheduleStartRetry()
101+
{
102+
// The internal (iBGP) peer is not reachable yet because the IGP has not finished
103+
// converging. Re-attempt the session start after a short delay instead of failing;
104+
// each retry re-checks reachability (see Idle::ManualStart).
105+
if (_ptrStartEvent == nullptr)
106+
_ptrStartEvent = new cMessage("BGP Start", START_EVENT_KIND);
107+
if (!_ptrStartEvent->isScheduled())
108+
bgpRouter.scheduleAt(simTime() + 1, _ptrStartEvent);
109+
_ptrStartEvent->setContextPointer(this);
110+
}
111+
100112
void BgpSession::cancelReconnect()
101113
{
102114
// Once (re-)established, drop any pending reconnect so a stale Start event cannot later

src/inet/routing/bgpv4/BgpSession.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class INET_API BgpSession : public cObject
6868

6969
void startConnection();
7070
void scheduleReconnect();
71+
// Retry starting this session shortly; used for an iBGP session whose peer is not
72+
// reachable yet because the IGP (e.g. OSPFv3) has not finished converging.
73+
void scheduleStartRetry();
74+
// True if the routing table currently has a route towards this session's peer.
75+
bool isPeerReachable() const { return bgpRouter.getIpRoutingTable()->getOutputInterfaceForDestination(_info.peerAddr) != nullptr; }
7176
void cancelReconnect();
7277
void restartHoldTimer();
7378
void restartKeepAliveTimer();

0 commit comments

Comments
 (0)