2 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.protocol.bgp.rib.impl;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkIdleState;
15 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkStateIsNotRestarting;
16 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkUpState;
17 import static org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages;
18 import static org.opendaylight.protocol.util.CheckUtil.readDataOperational;
19 import static org.opendaylight.protocol.util.CheckUtil.waitFutureSuccess;
21 import com.google.common.collect.ImmutableMap;
22 import io.netty.channel.Channel;
23 import io.netty.channel.ChannelFuture;
24 import java.net.InetSocketAddress;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
32 import java.util.Optional;
34 import java.util.stream.Collectors;
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.mockito.Mockito;
39 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
40 import org.opendaylight.protocol.bgp.mode.impl.add.all.paths.AllPathSelection;
41 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
42 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressNoZone;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.Open;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.OpenBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.ProtocolVersion;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.Update;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.open.message.BgpParameters;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.BgpOrigin;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv6AddressFamily;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.UnicastSubsequentAddressFamily;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71 import org.opendaylight.yangtools.yang.binding.Notification;
72 import org.opendaylight.yangtools.yang.common.Uint16;
73 import org.opendaylight.yangtools.yang.common.Uint8;
75 public class GracefulRestartTest extends AbstractAddPathTest {
77 private BGPSessionImpl session;
78 private BGPSessionImpl sessionv6;
80 private BGPPeer nonIpv4;
81 private final Set<TablesKey> afiSafiAdvertised = new HashSet<>();
82 private final Set<TablesKey> gracefulAfiSafiAdvertised = new HashSet<>();
83 private RIBImpl ribImpl;
84 private Channel serverChannel;
85 private final SimpleSessionListener listener = new SimpleSessionListener();
86 private final BgpParameters parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
87 private static final int DEFERRAL_TIMER = 5;
88 private static final RibId RIBID = new RibId("test-rib");
89 private static final Ipv4Prefix PREFIX2 = new Ipv4Prefix("2.2.2.2/32");
90 private static final Ipv6Prefix PREFIX3 = new Ipv6Prefix("dead:beef::/64");
91 private static final Ipv6AddressNoZone IPV6_NEXT_HOP = new Ipv6AddressNoZone("dead:beef::1");
92 private static final TablesKey IPV6_TABLES_KEY = new TablesKey(Ipv6AddressFamily.class,
93 UnicastSubsequentAddressFamily.class);
95 private static final InstanceIdentifier<LocRib> LOC_RIB_IID = InstanceIdentifier.builder(BgpRib.class)
96 .child(Rib.class, new RibKey(RIBID))
99 private static final InstanceIdentifier<Tables> IPV4_IID = LOC_RIB_IID.builder()
100 .child(Tables.class,TABLES_KEY)
102 private static final InstanceIdentifier<Tables> IPV6_IID = LOC_RIB_IID.builder()
103 .child(Tables.class, IPV6_TABLES_KEY)
108 public void setUp() throws Exception {
110 final Map<TablesKey, PathSelectionMode> pathTables
111 = ImmutableMap.of(TABLES_KEY, new AllPathSelection());
112 final ArrayList<BgpTableType> tableTypes = new ArrayList<>(TABLES_TYPE);
113 tableTypes.add(new BgpTableTypeImpl(Ipv6AddressFamily.class, UnicastSubsequentAddressFamily.class));
114 this.ribImpl = new RIBImpl(this.tableRegistry, RIBID, AS_NUMBER, BGP_ID, this.ribExtension,
115 this.serverDispatcher, this.codecsRegistry,
116 getDomBroker(), this.policies, tableTypes, pathTables);
118 this.ribImpl.instantiateServiceInstance();
119 final ChannelFuture channelFuture = this.serverDispatcher.createServer(
120 new InetSocketAddress(RIB_ID, PORT.toJava()));
121 waitFutureSuccess(channelFuture);
122 this.serverChannel = channelFuture.channel();
124 gracefulAfiSafiAdvertised.add(TABLES_KEY);
125 afiSafiAdvertised.add(TABLES_KEY);
126 afiSafiAdvertised.add(IPV6_TABLES_KEY);
127 final BgpPeer bgpPeer = Mockito.mock(BgpPeer.class);
128 Mockito.doReturn(GRACEFUL_RESTART_TIME).when(bgpPeer).getGracefulRestartTimer();
129 Mockito.doReturn(Optional.empty()).when(bgpPeer).getErrorHandling();
130 Mockito.doReturn(createParameter(false, true, Collections.singletonMap(TABLES_KEY, false))
131 .getOptionalCapabilities()).when(bgpPeer).getBgpFixedCapabilities();
132 this.peer = configurePeer(this.tableRegistry, PEER1, this.ribImpl, parameters, PeerRole.Ibgp,
133 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised, Collections.emptyMap(), bgpPeer);
134 this.session = createPeerSession(PEER1, parameters, this.listener);
139 public void tearDown() throws Exception {
140 waitFutureSuccess(this.serverChannel.close());
141 this.session.close();
146 * Test graceful restart of a non-IPv4 peer.
147 * {@link BGPPeer#releaseConnection(boolean)} should not throw NPE and binding chain should be closed
150 * @throws InterruptedException on create peer session.
153 public void nonIpv4PeerGracefulRestart() throws InterruptedException {
154 BgpParameters parametersv6 = PeerUtil.createBgpParameters(Collections.singletonList(IPV6_TABLES_KEY),
155 Collections.emptyList(), Collections.singletonMap(IPV6_TABLES_KEY, true),
156 GRACEFUL_RESTART_TIME);
157 gracefulAfiSafiAdvertised.add(IPV6_TABLES_KEY);
158 this.nonIpv4 = configurePeer(this.tableRegistry, PEER2, this.ribImpl, parametersv6, PeerRole.Ibgp,
159 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
160 this.sessionv6 = createPeerSession(PEER2, parametersv6, this.listener);
161 final Open open = createClassicOpen(true);
162 this.sessionv6.writeAndFlush(open);
163 checkIdleState(this.nonIpv4);
164 synchronized (this.nonIpv4) {
165 assertNull(this.nonIpv4.ribOutChain);
170 * Test correct behavior when connection restart is unnoticed.
171 * "Correct" means that the previous TCP session MUST be closed, and the new one retained.
172 * Since the previous connection is considered to be terminated, no NOTIFICATION message should be sent.
175 public void resetConnectionOnOpenTest() {
177 checkReceivedMessages(this.listener, 2);
178 final Open open = createClassicOpen(true);
179 this.session.writeAndFlush(open);
180 checkIdleState(this.peer);
181 checkReceivedMessages(this.listener, 2);
185 * Test that routes from peer that has advertised the Graceful Restart Capability MUST be retained
186 * for all the address families that were previously received in the Graceful Restart Capability.
188 * @throws Exception on reading Rib failure
191 public void retainRoutesOnPeerRestartTest() throws Exception {
192 final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
193 final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
194 insertRoutes(ipv4Prefixes, ipv6Prefixes);
195 checkLocRibIpv4Routes(2);
196 checkLocRibIpv6Routes(1);
198 this.session.close();
199 checkIdleState(this.peer);
200 checkLocRibIpv4Routes(2);
201 checkLocRibIpv6Routes(0);
205 * If the session does not get re-established within the "Restart Time"
206 * that the peer advertised previously, the Receiving Speaker MUST
207 * delete all the stale routes from the peer that it is retaining.
209 * @throws Exception on reading Rib failure
212 public void removeRoutesOnHoldTimeExpireTest() throws Exception {
213 retainRoutesOnPeerRestartTest();
214 checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
215 checkLocRibIpv4Routes(0);
216 checkLocRibIpv6Routes(0);
220 * Once the session is re-established, if the Graceful
221 * Restart Capability is not received in the re-established session at
222 * all, then the Receiving Speaker MUST immediately remove all the stale
223 * routes from the peer that it is retaining for that address family.
225 * @throws Exception on reading Rib failure
228 public void removeRoutesOnMissingGracefulRestartTest() throws Exception {
229 retainRoutesOnPeerRestartTest();
230 this.session = createPeerSession(PEER1, createParameter(false, true, null), this.listener);
231 checkUpState(listener);
232 checkLocRibIpv4Routes(0);
233 checkLocRibIpv6Routes(0);
237 * Once the session is re-established, if a specific address family is not included
238 * in the newly received Graceful Restart Capability, then the Receiving Speaker
239 * MUST immediately remove all the stale routes from the peer that it is retaining
240 * for that address family.
243 * @throws Exception on reading Rib failure
246 public void removeRoutesOnMissingGracefulRestartAfiSafiTest() throws Exception {
247 retainRoutesOnPeerRestartTest();
248 this.session = createPeerSession(PEER1, createParameter(false, true,
249 Collections.singletonMap(TABLES_KEY, false)), this.listener);
250 checkUpState(listener);
251 checkUpState(this.peer);
252 checkLocRibIpv4Routes(0);
253 checkLocRibIpv6Routes(0);
257 * Once the End-of-RIB marker for an address family is received from the peer, it MUST
258 * immediately remove any routes from the peer that are still marked as stale for that
261 * @throws Exception on reading Rib failure
264 public void removeStaleRoutesAfterRestartTest() throws Exception {
265 retainRoutesOnPeerRestartTest();
266 this.session = createPeerSession(PEER1, createParameter(false, true,
267 Collections.singletonMap(TABLES_KEY, true)), this.listener);
268 checkUpState(this.listener);
269 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
270 insertRoutes(ipv4prefixes, null);
271 insertRoutes(null, null);
272 checkLocRibIpv4Routes(1);
273 checkLocRibIpv6Routes(0);
277 * Perform local graceful restart and verify routes are preserved.
279 * @throws Exception on reading Rib failure
282 public void performLocalGracefulRestart() throws Exception {
283 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
284 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
285 insertRoutes(ipv4prefixes, ipv6prefixes);
286 checkLocRibIpv4Routes(2);
287 checkLocRibIpv6Routes(1);
289 this.peer.restartGracefully(DEFERRAL_TIMER).get();
290 this.session = createPeerSession(PEER1, this.parameters, this.listener);
291 checkUpState(this.listener);
292 checkUpState(this.peer);
293 checkLocRibIpv4Routes(2);
294 checkLocRibIpv6Routes(0);
298 * Wait with route selection until EOT is received.
300 * @throws Exception on reading Rib failure
303 public void waitForEORonLocalGracefulRestart() throws Exception {
304 performLocalGracefulRestart();
305 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
306 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
307 insertRoutes(ipv4prefixes, ipv6prefixes);
308 checkLocRibIpv4Routes(2);
309 checkLocRibIpv6Routes(0);
310 insertRoutes(null, null);
311 checkLocRibIpv4Routes(2);
312 checkLocRibIpv6Routes(1);
316 * Wait with route selection until deferral time is expired.
318 * @throws Exception on reading Rib failure
321 public void waitForDeferralTimerOnLocalGracefulRestart() throws Exception {
322 performLocalGracefulRestart();
323 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
324 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
325 insertRoutes(ipv4prefixes, ipv6prefixes);
326 checkLocRibIpv4Routes(2);
327 checkLocRibIpv6Routes(0);
328 checkStateIsNotRestarting(this.peer, DEFERRAL_TIMER);
329 checkLocRibIpv4Routes(2);
330 checkLocRibIpv6Routes(1);
334 * After graceful restart is performed from peer side we have to re-advertise routes followed by
337 * @throws Exception on reading Rib failure
340 public void verifySendEORafterRestartTest() throws Exception {
341 final SimpleSessionListener listener2 = new SimpleSessionListener();
342 configurePeer(this.tableRegistry, PEER2, this.ribImpl, this.parameters, PeerRole.Ebgp,
343 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
344 final BGPSessionImpl session2 = createPeerSession(PEER2, this.parameters, listener2);
345 final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
346 final List<Ipv4Prefix> ipv4Prefixes2 = Arrays.asList(new Ipv4Prefix(PREFIX2));
347 final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
348 insertRoutes(ipv4Prefixes, ipv6Prefixes);
349 insertRoutes(ipv4Prefixes2, PEER2, null, null, session2, BgpOrigin.Egp);
350 checkLocRibIpv4Routes(2);
351 checkLocRibIpv6Routes(1);
352 org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages(this.listener, 3);
353 // verify sending of Ipv4 EOT, Ipv6 EOT and Ipv4 update with route
354 checkReceivedMessages(this.listener, 3);
355 assertTrue(this.listener.getListMsg().get(0) instanceof Update);
356 assertTrue(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(0)));
357 assertTrue(this.listener.getListMsg().get(1) instanceof Update);
358 assertTrue(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(1)));
359 assertTrue(this.listener.getListMsg().get(2) instanceof Update);
360 assertFalse(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(2)));
362 this.session.close();
363 checkIdleState(this.peer);
364 checkLocRibIpv4Routes(2);
365 checkLocRibIpv6Routes(0);
366 // verify nothing new was sent
367 checkReceivedMessages(this.listener, 3);
369 this.session = createPeerSession(PEER1, createParameter(false, true,
370 Collections.singletonMap(TABLES_KEY, true)), this.listener);
371 checkUpState(listener);
372 checkUpState(this.peer);
373 org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages(this.listener, 6);
374 // verify sending of Ipv4 update with route, Ipv4 EOT and Ipv6 EOT; order can vary based on ODTC order
375 final List<Notification> subList = this.listener.getListMsg().subList(3, 6);
377 int routeUpdateCount = 0;
378 for (Notification message : subList) {
379 if (BgpPeerUtil.isEndOfRib((Update) message)) {
385 assertEquals(2, eotCount);
386 assertEquals(1, routeUpdateCount);
389 private void checkLocRibIpv4Routes(final int expectedRoutesOnDS) throws Exception {
390 readDataOperational(getDataBroker(), IPV4_IID, table -> {
392 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
393 if (routesCase != null && routesCase.getIpv4Routes() != null
394 && routesCase.getIpv4Routes().getIpv4Route() != null) {
395 size = routesCase.getIpv4Routes().getIpv4Route().size();
397 assertEquals(expectedRoutesOnDS, size);
402 private void checkLocRibIpv6Routes(final int expectedRoutesOnDS) throws Exception {
403 readDataOperational(getDataBroker(), IPV6_IID, table -> {
405 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
406 if (routesCase != null && routesCase.getIpv6Routes() != null
407 && routesCase.getIpv6Routes().getIpv6Route() != null) {
408 size = routesCase.getIpv6Routes().getIpv6Route().size();
410 assertEquals(expectedRoutesOnDS, size);
415 private void insertRoutes(final List<Ipv4Prefix> ipv4prefixes, final List<Ipv6Prefix> ipv6prefixes) {
416 insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session, BgpOrigin.Igp);
419 private static void insertRoutes(final List<Ipv4Prefix> ipv4prefixes, final Ipv4AddressNoZone ipv4NeighborAddress,
420 final List<Ipv6Prefix> ipv6prefixes, final Ipv6AddressNoZone ipv6NeighborAddress,
421 final BGPSessionImpl session, final BgpOrigin peerRole) {
422 if (ipv4prefixes == null && ipv6prefixes == null) {
423 waitFutureSuccess(session.writeAndFlush(BgpPeerUtil.createEndOfRib(TABLES_KEY)));
424 waitFutureSuccess(session.writeAndFlush(BgpPeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
428 if (ipv4prefixes != null && !ipv4prefixes.isEmpty()) {
429 final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddressNoZone(ipv4NeighborAddress),
430 ipv4prefixes.stream()
432 .collect(Collectors.toList()));
433 final Update update1 = PeerUtil.createUpdate(peerRole, Collections.emptyList(), 100, reachIpv4, null);
434 waitFutureSuccess(session.writeAndFlush(update1));
437 if (ipv6prefixes != null && !ipv4prefixes.isEmpty()) {
438 final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddressNoZone(ipv6NeighborAddress),
439 ipv6prefixes.stream()
441 .collect(Collectors.toList()));
442 final Update update2 = PeerUtil.createUpdate(peerRole, Collections.emptyList(), 100, reachIpv6, null);
443 waitFutureSuccess(session.writeAndFlush(update2));
447 private static Open createClassicOpen(final boolean addGraceful) {
448 final Map<TablesKey, Boolean> graceful = new HashMap<>();
450 graceful.put(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class), true);
452 return new OpenBuilder()
453 .setMyAsNumber(Uint16.valueOf(AS))
454 .setHoldTimer(Uint16.valueOf(HOLDTIMER))
455 .setVersion(new ProtocolVersion(Uint8.valueOf(4)))
456 .setBgpParameters(Collections.singletonList(createParameter(false, true, graceful)))
457 .setBgpIdentifier(PEER1)