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.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkIdleState;
11 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkStateIsNotRestarting;
12 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkUpState;
13 import static org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages;
14 import static org.opendaylight.protocol.util.CheckUtil.readDataOperational;
15 import static org.opendaylight.protocol.util.CheckUtil.waitFutureSuccess;
17 import com.google.common.collect.ImmutableMap;
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import java.net.InetSocketAddress;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
29 import java.util.concurrent.ExecutionException;
30 import java.util.stream.Collectors;
31 import org.junit.After;
32 import org.junit.Assert;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.mockito.Mockito;
36 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
37 import org.opendaylight.protocol.bgp.mode.impl.add.all.paths.AllPathSelection;
38 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
39 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Open;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.ProtocolVersion;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.BgpOrigin;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
69 public class GracefulRestartTest extends AbstractAddPathTest {
71 private BGPSessionImpl session;
72 private BGPSessionImpl session2;
74 private BgpPeer bgpPeer;
75 private BgpParameters parameters;
76 private final Set<TablesKey> afiSafiAdvertised = new HashSet<>();
77 private final Set<TablesKey> gracefulAfiSafiAdvertised = new HashSet<>();
78 private RIBImpl ribImpl;
79 private Channel serverChannel;
80 private SimpleSessionListener listener = new SimpleSessionListener();
81 private SimpleSessionListener listener2 = new SimpleSessionListener();
82 private static final int DEFERRAL_TIMER = 5;
83 private static final RibId RIBID = new RibId("test-rib");
84 private final Ipv4Prefix PREFIX2 = new Ipv4Prefix("2.2.2.2/32");
85 private final Ipv6Prefix PREFIX3 = new Ipv6Prefix("dead:beef::/64");
86 private final Ipv6Address IPV6_NEXT_HOP = new Ipv6Address("dead:beef::1");
87 private static final TablesKey IPV6_TABLES_KEY = new TablesKey(Ipv6AddressFamily.class,
88 UnicastSubsequentAddressFamily.class);
90 private static final InstanceIdentifier<LocRib> LOC_RIB_IID = InstanceIdentifier.builder(BgpRib.class)
91 .child(Rib.class, new RibKey(RIBID))
94 private static final InstanceIdentifier<Tables> IPV4_IID = LOC_RIB_IID.builder()
95 .child(Tables.class,TABLES_KEY)
97 private static final InstanceIdentifier<Tables> IPV6_IID = LOC_RIB_IID.builder()
98 .child(Tables.class, IPV6_TABLES_KEY)
103 public void setUp() throws Exception {
105 final Map<TablesKey, PathSelectionMode> pathTables
106 = ImmutableMap.of(TABLES_KEY, new AllPathSelection());
107 final ArrayList<BgpTableType> tableTypes = new ArrayList<>(TABLES_TYPE);
108 tableTypes.add(new BgpTableTypeImpl(Ipv6AddressFamily.class, UnicastSubsequentAddressFamily.class));
109 this.ribImpl = new RIBImpl(this.tableRegistry, RIBID, AS_NUMBER, BGP_ID, this.ribExtension,
110 this.serverDispatcher, this.codecsRegistry,
111 getDomBroker(), getDataBroker(), this.policies, tableTypes, pathTables);
113 this.ribImpl.instantiateServiceInstance();
114 this.ribImpl.onGlobalContextUpdated(this.schemaService.getGlobalContext());
115 final ChannelFuture channelFuture = this.serverDispatcher.createServer(new InetSocketAddress(RIB_ID, PORT));
116 waitFutureSuccess(channelFuture);
117 this.serverChannel = channelFuture.channel();
119 gracefulAfiSafiAdvertised.add(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class));
120 afiSafiAdvertised.addAll(gracefulAfiSafiAdvertised);
121 afiSafiAdvertised.add(IPV6_TABLES_KEY);
122 this.parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
123 this.bgpPeer = Mockito.mock(BgpPeer.class);
124 Mockito.doReturn(GRACEFUL_RESTART_TIME).when(this.bgpPeer).getGracefulRestartTimer();
125 Mockito.doReturn(createParameter(false, true, Collections.singletonMap(TABLES_KEY, false))
126 .getOptionalCapabilities()).when(this.bgpPeer).getBgpFixedCapabilities();
127 this.peer = configurePeer(this.tableRegistry, PEER1, this.ribImpl, parameters, PeerRole.Ibgp,
128 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised, bgpPeer);
129 this.session = createPeerSession(PEER1, parameters, this.listener);
130 final BgpParameters parameters2 = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
131 configurePeer(this.tableRegistry, PEER2, this.ribImpl, parameters2, PeerRole.Ibgp,
132 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
133 this.session2 = createPeerSession(PEER2, parameters2, this.listener2);
137 public void tearDown() throws ExecutionException, InterruptedException {
138 waitFutureSuccess(this.serverChannel.close());
139 this.session.close();
144 * Test correct behavior when connection restart is unnoticed.
145 * "Correct" means that the previous TCP session MUST be closed, and the new one retained.
146 * Since the previous connection is considered to be terminated, no NOTIFICATION message should be sent.
149 public void resetConnectionOnOpenTest() {
151 final Open open = createClassicOpen(true);
152 this.session.writeAndFlush(open);
153 checkIdleState(this.peer);
154 checkReceivedMessages(this.listener, 0);
158 * Test that routes from peer that has advertised the Graceful Restart Capability MUST be retained
159 * for all the address families that were previously received in the Graceful Restart Capability.
161 * @throws Exception on reading Rib failure
164 public void retainRoutesOnPeerRestartTest() throws Exception {
165 final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
166 final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
167 insertRoutes(ipv4Prefixes, PEER1, ipv6Prefixes, IPV6_NEXT_HOP, this.session);
168 checkLocRibIpv4Routes(2);
169 checkLocRibIpv6Routes(1);
171 this.session.close();
172 checkIdleState(this.peer);
173 checkLocRibIpv4Routes(2);
174 checkLocRibIpv6Routes(0);
178 * If the session does not get re-established within the "Restart Time"
179 * that the peer advertised previously, the Receiving Speaker MUST
180 * delete all the stale routes from the peer that it is retaining.
182 * @throws Exception on reading Rib failure
185 public void removeRoutesOnHoldTimeExpireTest() throws Exception {
186 retainRoutesOnPeerRestartTest();
187 checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
188 checkLocRibIpv4Routes(0);
189 checkLocRibIpv6Routes(0);
193 * Once the session is re-established, if the Graceful
194 * Restart Capability is not received in the re-established session at
195 * all, then the Receiving Speaker MUST immediately remove all the stale
196 * routes from the peer that it is retaining for that address family.
198 * @throws Exception on reading Rib failure
201 public void removeRoutesOnMissingGracefulRestartTest() throws Exception {
202 retainRoutesOnPeerRestartTest();
203 this.session = createPeerSession(PEER1, createParameter(false, true, null), this.listener);
204 checkUpState(listener);
205 checkLocRibIpv4Routes(0);
206 checkLocRibIpv6Routes(0);
210 * Once the session is re-established, if a specific address family is not included
211 * in the newly received Graceful Restart Capability, then the Receiving Speaker
212 * MUST immediately remove all the stale routes from the peer that it is retaining
213 * for that address family.
216 * @throws Exception on reading Rib failure
219 public void removeRoutesOnMissingGracefulRestartAfiSafiTest() throws Exception {
220 retainRoutesOnPeerRestartTest();
221 this.session = createPeerSession(PEER1, createParameter(false, true, Collections.singletonMap(TABLES_KEY, false)),
223 checkUpState(listener);
224 checkUpState(this.peer);
225 checkLocRibIpv4Routes(0);
226 checkLocRibIpv6Routes(0);
230 * Once the End-of-RIB marker for an address family is received from the peer, it MUST
231 * immediately remove any routes from the peer that are still marked as stale for that
234 * @throws Exception on reading Rib failure
237 public void removeStaleRoutesAfterRestartTest() throws Exception {
238 retainRoutesOnPeerRestartTest();
239 this.session = createPeerSession(PEER1, createParameter(false, true,
240 Collections.singletonMap(TABLES_KEY, true)), this.listener);
241 checkUpState(this.listener);
242 insertRoutes(Collections.singletonList(new Ipv4Prefix(PREFIX1)), PEER1, null, null, this.session);
243 insertRoutes(null, null, null, null, this.session);
244 checkLocRibIpv4Routes(1);
245 checkLocRibIpv6Routes(0);
249 * Perform local graceful restart and verify routes are preserved.
251 * @throws Exception on reading Rib failure
254 public void performLocalGracefulRestart() throws Exception {
255 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
256 final List<Ipv4Prefix> ipv4prefixes2 = Arrays.asList(new Ipv4Prefix(PREFIX2));
257 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
258 insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
259 insertRoutes(ipv4prefixes2, PEER2, null, null, this.session2);
260 checkLocRibIpv4Routes(2);
261 checkLocRibIpv6Routes(1);
263 this.peer.restartGracefully(DEFERRAL_TIMER).get();
264 this.session = createPeerSession(PEER1, this.parameters, this.listener);
265 checkUpState(this.listener);
266 checkUpState(this.peer);
267 checkLocRibIpv4Routes(2);
268 checkLocRibIpv6Routes(0);
272 * Wait with route selection until EOT is received.
274 * @throws Exception on reading Rib failure
277 public void waitForEORonLocalGracefulRestart() throws Exception {
278 performLocalGracefulRestart();
279 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
280 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
281 insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
282 checkLocRibIpv4Routes(2);
283 checkLocRibIpv6Routes(0);
284 insertRoutes(null, null,null, null, this.session);
285 checkLocRibIpv4Routes(2);
286 checkLocRibIpv6Routes(1);
290 * Wait with route selection until deferral time is expired.
292 * @throws Exception on reading Rib failure
295 public void waitForDeferralTimerOnLocalGracefulRestart() throws Exception {
296 performLocalGracefulRestart();
297 final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
298 final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
299 insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
300 checkLocRibIpv4Routes(2);
301 checkLocRibIpv6Routes(0);
302 checkStateIsNotRestarting(this.peer, DEFERRAL_TIMER);
303 checkLocRibIpv4Routes(2);
304 checkLocRibIpv6Routes(1);
307 private void checkLocRibIpv4Routes(final int expectedRoutesOnDS) throws Exception {
308 readDataOperational(getDataBroker(), IPV4_IID, table -> {
310 final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
311 if (routesCase != null && routesCase.getIpv4Routes() != null &&
312 routesCase.getIpv4Routes().getIpv4Route() != null) {
313 size = routesCase.getIpv4Routes().getIpv4Route().size();
315 Assert.assertEquals(expectedRoutesOnDS, size);
320 private void checkLocRibIpv6Routes(final int expectedRoutesOnDS) throws Exception {
321 readDataOperational(getDataBroker(), IPV6_IID, table -> {
323 final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
324 if (routesCase != null && routesCase.getIpv6Routes() != null &&
325 routesCase.getIpv6Routes().getIpv6Route() != null) {
326 size = routesCase.getIpv6Routes().getIpv6Route().size();
328 Assert.assertEquals(expectedRoutesOnDS, size);
333 private void insertRoutes(List<Ipv4Prefix> ipv4prefixes, Ipv4Address ipv4NextHop, List<Ipv6Prefix> ipv6prefixes,
334 Ipv6Address ipv6NextHop, BGPSessionImpl session) {
335 if (ipv4prefixes == null && ipv6prefixes == null) {
336 waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(TABLES_KEY)));
337 waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
341 if (ipv4prefixes != null && !ipv4prefixes.isEmpty()) {
342 final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddress(ipv4NextHop), 0,
343 ipv4prefixes.stream()
345 .collect(Collectors.toList()));
346 final Update update1 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv4, null);
347 waitFutureSuccess(session.writeAndFlush(update1));
350 if (ipv6prefixes != null && !ipv4prefixes.isEmpty()) {
351 final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddress(ipv6NextHop), 0,
352 ipv6prefixes.stream()
354 .collect(Collectors.toList()));
355 final Update update2 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv6, null);
356 waitFutureSuccess(session.writeAndFlush(update2));
360 private static Open createClassicOpen(boolean addGraceful) {
361 final Map<TablesKey, Boolean> graceful = new HashMap<>();
363 graceful.put(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class), true);
365 return new OpenBuilder()
366 .setMyAsNumber((int) AS)
367 .setHoldTimer(HOLDTIMER)
368 .setVersion(new ProtocolVersion((short) 4))
369 .setBgpParameters(Collections.singletonList(createParameter(false, true, graceful)))
370 .setBgpIdentifier(PEER1)