send EOR marker after initial route exchange
[bgpcep.git] / bgp / rib-impl / src / test / java / org / opendaylight / protocol / bgp / rib / impl / GracefulRestartTest.java
1 /*
2  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.rib.impl;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertTrue;
13 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkIdleState;
14 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkStateIsNotRestarting;
15 import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkUpState;
16 import static org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages;
17 import static org.opendaylight.protocol.util.CheckUtil.readDataOperational;
18 import static org.opendaylight.protocol.util.CheckUtil.waitFutureSuccess;
19
20 import com.google.common.collect.ImmutableMap;
21 import io.netty.channel.Channel;
22 import io.netty.channel.ChannelFuture;
23 import java.net.InetSocketAddress;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.concurrent.ExecutionException;
33 import java.util.stream.Collectors;
34 import org.junit.After;
35 import org.junit.Before;
36 import org.junit.Test;
37 import org.mockito.Mockito;
38 import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
39 import org.opendaylight.protocol.bgp.mode.impl.add.all.paths.AllPathSelection;
40 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
41 import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv4RoutesCase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.bgp.rib.rib.loc.rib.tables.routes.Ipv6RoutesCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Open;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.ProtocolVersion;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.RibId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.RibKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.BgpOrigin;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
69 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
70 import org.opendaylight.yangtools.yang.binding.Notification;
71
72 public class GracefulRestartTest extends AbstractAddPathTest {
73
74     private BGPSessionImpl session;
75     private BGPPeer peer;
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 final BgpParameters parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
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);
89
90     private static final InstanceIdentifier<LocRib> LOC_RIB_IID = InstanceIdentifier.builder(BgpRib.class)
91             .child(Rib.class, new RibKey(RIBID))
92             .child(LocRib.class)
93             .build();
94     private static final InstanceIdentifier<Tables> IPV4_IID = LOC_RIB_IID.builder()
95             .child(Tables.class,TABLES_KEY)
96             .build();
97     private static final InstanceIdentifier<Tables> IPV6_IID = LOC_RIB_IID.builder()
98             .child(Tables.class, IPV6_TABLES_KEY)
99             .build();
100
101     @Override
102     @Before
103     public void setUp() throws Exception {
104         super.setUp();
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);
112
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();
118
119         gracefulAfiSafiAdvertised.add(TABLES_KEY);
120         afiSafiAdvertised.add(TABLES_KEY);
121         afiSafiAdvertised.add(IPV6_TABLES_KEY);
122         final BgpPeer bgpPeer = Mockito.mock(BgpPeer.class);
123         Mockito.doReturn(GRACEFUL_RESTART_TIME).when(bgpPeer).getGracefulRestartTimer();
124         Mockito.doReturn(createParameter(false, true, Collections.singletonMap(TABLES_KEY, false))
125                 .getOptionalCapabilities()).when(bgpPeer).getBgpFixedCapabilities();
126         this.peer = configurePeer(this.tableRegistry, PEER1, this.ribImpl, parameters, PeerRole.Ibgp,
127                 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised, bgpPeer);
128         this.session = createPeerSession(PEER1, parameters, this.listener);
129     }
130
131     @After
132     public void tearDown() throws ExecutionException, InterruptedException {
133         waitFutureSuccess(this.serverChannel.close());
134         this.session.close();
135         super.tearDown();
136     }
137
138     /**
139      * Test correct behavior when connection restart is unnoticed.
140      * "Correct" means that the previous TCP session MUST be closed, and the new one retained.
141      * Since the previous connection is considered to be terminated, no NOTIFICATION message should be sent.
142      */
143     @Test
144     public void resetConnectionOnOpenTest() {
145
146         final Open open = createClassicOpen(true);
147         this.session.writeAndFlush(open);
148         checkIdleState(this.peer);
149         checkReceivedMessages(this.listener, 2);
150     }
151
152     /**
153      * Test that routes from peer that has advertised the Graceful Restart Capability MUST be retained
154      * for all the address families that were previously received in the Graceful Restart Capability.
155      *
156      * @throws Exception on reading Rib failure
157      */
158     @Test
159     public void retainRoutesOnPeerRestartTest() throws Exception {
160         final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
161         final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
162         insertRoutes(ipv4Prefixes, ipv6Prefixes);
163         checkLocRibIpv4Routes(2);
164         checkLocRibIpv6Routes(1);
165
166         this.session.close();
167         checkIdleState(this.peer);
168         checkLocRibIpv4Routes(2);
169         checkLocRibIpv6Routes(0);
170     }
171
172     /**
173      * If the session does not get re-established within the "Restart Time"
174      * that the peer advertised previously, the Receiving Speaker MUST
175      * delete all the stale routes from the peer that it is retaining.
176      *
177      * @throws Exception on reading Rib failure
178      */
179     @Test
180     public void removeRoutesOnHoldTimeExpireTest() throws Exception {
181         retainRoutesOnPeerRestartTest();
182         checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
183         checkLocRibIpv4Routes(0);
184         checkLocRibIpv6Routes(0);
185     }
186
187     /**
188      * Once the session is re-established, if the Graceful
189      * Restart Capability is not received in the re-established session at
190      * all, then the Receiving Speaker MUST immediately remove all the stale
191      * routes from the peer that it is retaining for that address family.
192      *
193      * @throws Exception on reading Rib failure
194      */
195     @Test
196     public void removeRoutesOnMissingGracefulRestartTest() throws Exception {
197         retainRoutesOnPeerRestartTest();
198         this.session = createPeerSession(PEER1, createParameter(false, true, null), this.listener);
199         checkUpState(listener);
200         checkLocRibIpv4Routes(0);
201         checkLocRibIpv6Routes(0);
202     }
203
204     /**
205      * Once the session is re-established, if a specific address family is not included
206      * in the newly received Graceful Restart Capability, then the Receiving Speaker
207      * MUST immediately remove all the stale routes from the peer that it is retaining
208      * for that address family.
209      *
210      *
211      * @throws Exception on reading Rib failure
212      */
213     @Test
214     public void removeRoutesOnMissingGracefulRestartAfiSafiTest() throws Exception {
215         retainRoutesOnPeerRestartTest();
216         this.session = createPeerSession(PEER1, createParameter(false, true,
217                 Collections.singletonMap(TABLES_KEY, false)), this.listener);
218         checkUpState(listener);
219         checkUpState(this.peer);
220         checkLocRibIpv4Routes(0);
221         checkLocRibIpv6Routes(0);
222     }
223
224     /**
225      * Once the End-of-RIB marker for an address family is received from the peer, it MUST
226      * immediately remove any routes from the peer that are still marked as stale for that
227      * address family.
228      *
229      * @throws Exception on reading Rib failure
230      */
231     @Test
232     public void removeStaleRoutesAfterRestartTest() throws Exception {
233         retainRoutesOnPeerRestartTest();
234         this.session = createPeerSession(PEER1, createParameter(false, true,
235                 Collections.singletonMap(TABLES_KEY, true)), this.listener);
236         checkUpState(this.listener);
237         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
238         insertRoutes(ipv4prefixes, null);
239         insertRoutes(null, null);
240         checkLocRibIpv4Routes(1);
241         checkLocRibIpv6Routes(0);
242     }
243
244     /**
245      * Perform local graceful restart and verify routes are preserved.
246      *
247      * @throws Exception on reading Rib failure
248      */
249     @Test
250     public void performLocalGracefulRestart() throws Exception {
251         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
252         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
253         insertRoutes(ipv4prefixes, ipv6prefixes);
254         checkLocRibIpv4Routes(2);
255         checkLocRibIpv6Routes(1);
256
257         this.peer.restartGracefully(DEFERRAL_TIMER).get();
258         this.session = createPeerSession(PEER1, this.parameters, this.listener);
259         checkUpState(this.listener);
260         checkUpState(this.peer);
261         checkLocRibIpv4Routes(2);
262         checkLocRibIpv6Routes(0);
263     }
264
265     /**
266      * Wait with route selection until EOT is received.
267      *
268      * @throws Exception on reading Rib failure
269      */
270     @Test
271     public void waitForEORonLocalGracefulRestart() throws Exception {
272         performLocalGracefulRestart();
273         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
274         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
275         insertRoutes(ipv4prefixes, ipv6prefixes);
276         checkLocRibIpv4Routes(2);
277         checkLocRibIpv6Routes(0);
278         insertRoutes(null, null);
279         checkLocRibIpv4Routes(2);
280         checkLocRibIpv6Routes(1);
281     }
282
283     /**
284      * Wait with route selection until deferral time is expired.
285      *
286      * @throws Exception on reading Rib failure
287      */
288     @Test
289     public void waitForDeferralTimerOnLocalGracefulRestart() throws Exception {
290         performLocalGracefulRestart();
291         final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
292         final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
293         insertRoutes(ipv4prefixes, ipv6prefixes);
294         checkLocRibIpv4Routes(2);
295         checkLocRibIpv6Routes(0);
296         checkStateIsNotRestarting(this.peer, DEFERRAL_TIMER);
297         checkLocRibIpv4Routes(2);
298         checkLocRibIpv6Routes(1);
299     }
300
301     /**
302      * After graceful restart is performed from peer side we have to re-advertise routes followed by
303      * End-of-RIB marker.
304      *
305      * @throws Exception on reading Rib failure
306      */
307     @Test
308     public void verifySendEORafterRestartTest() throws Exception {
309         final SimpleSessionListener listener2 = new SimpleSessionListener();
310         configurePeer(this.tableRegistry, PEER2, this.ribImpl, this.parameters, PeerRole.Ebgp,
311                 this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
312         final BGPSessionImpl session2 = createPeerSession(PEER2, this.parameters, listener2);
313         final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
314         final List<Ipv4Prefix> ipv4Prefixes2 = Arrays.asList(new Ipv4Prefix(PREFIX2));
315         final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
316         insertRoutes(ipv4Prefixes, ipv6Prefixes);
317         insertRoutes(ipv4Prefixes2, PEER2, null, null, session2, BgpOrigin.Egp);
318         checkLocRibIpv4Routes(2);
319         checkLocRibIpv6Routes(1);
320         org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages(this.listener, 3);
321         // verify sending of Ipv4 EOT, Ipv6 EOT and Ipv4 update with route
322         checkReceivedMessages(this.listener, 3);
323         assertTrue(this.listener.getListMsg().get(0) instanceof Update);
324         assertTrue(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(0)));
325         assertTrue(this.listener.getListMsg().get(1) instanceof Update);
326         assertTrue(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(1)));
327         assertTrue(this.listener.getListMsg().get(2) instanceof Update);
328         assertFalse(BgpPeerUtil.isEndOfRib((Update)this.listener.getListMsg().get(2)));
329
330         this.session.close();
331         checkIdleState(this.peer);
332         checkLocRibIpv4Routes(2);
333         checkLocRibIpv6Routes(0);
334         // verify nothing new was sent
335         checkReceivedMessages(this.listener, 3);
336
337         this.session = createPeerSession(PEER1, createParameter(false, true,
338                 Collections.singletonMap(TABLES_KEY, true)), this.listener);
339         checkUpState(listener);
340         checkUpState(this.peer);
341         org.opendaylight.protocol.util.CheckUtil.checkReceivedMessages(this.listener, 6);
342         // verify sending of Ipv4 update with route, Ipv4 EOT and Ipv6 EOT; order can vary based on ODTC order
343         final List<Notification> subList = this.listener.getListMsg().subList(3, 6);
344         int EOTCount = 0;
345         int routeUpdateCount = 0;
346         for (Notification message : subList) {
347             if (BgpPeerUtil.isEndOfRib((Update) message)) {
348                 EOTCount++;
349             } else {
350                 routeUpdateCount++;
351             }
352         }
353         assertEquals(2, EOTCount);
354         assertEquals(1, routeUpdateCount);
355     }
356
357     private void checkLocRibIpv4Routes(final int expectedRoutesOnDS) throws Exception {
358         readDataOperational(getDataBroker(), IPV4_IID, table -> {
359             int size = 0;
360             final Ipv4RoutesCase routesCase = (Ipv4RoutesCase) table.getRoutes();
361             if (routesCase != null && routesCase.getIpv4Routes() != null &&
362                     routesCase.getIpv4Routes().getIpv4Route() != null) {
363                 size = routesCase.getIpv4Routes().getIpv4Route().size();
364             }
365             assertEquals(expectedRoutesOnDS, size);
366             return table;
367         });
368     }
369
370     private void checkLocRibIpv6Routes(final int expectedRoutesOnDS) throws Exception {
371         readDataOperational(getDataBroker(), IPV6_IID, table -> {
372             int size = 0;
373             final Ipv6RoutesCase routesCase = (Ipv6RoutesCase) table.getRoutes();
374             if (routesCase != null && routesCase.getIpv6Routes() != null &&
375                     routesCase.getIpv6Routes().getIpv6Route() != null) {
376                 size = routesCase.getIpv6Routes().getIpv6Route().size();
377             }
378             assertEquals(expectedRoutesOnDS, size);
379             return table;
380         });
381     }
382
383     private void insertRoutes(List<Ipv4Prefix> ipv4prefixes, List<Ipv6Prefix> ipv6prefixes) {
384         insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session, BgpOrigin.Igp);
385     }
386
387     private void insertRoutes(final List<Ipv4Prefix> ipv4prefixes, final Ipv4Address ipv4NeighborAddress,
388                               final List<Ipv6Prefix> ipv6prefixes, final Ipv6Address ipv6NeighborAddress,
389                               final BGPSessionImpl session, final BgpOrigin peerRole) {
390         if (ipv4prefixes == null && ipv6prefixes == null) {
391             waitFutureSuccess(session.writeAndFlush(BgpPeerUtil.createEndOfRib(TABLES_KEY)));
392             waitFutureSuccess(session.writeAndFlush(BgpPeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
393             return;
394         }
395
396         if (ipv4prefixes != null && !ipv4prefixes.isEmpty()) {
397             final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddress(ipv4NeighborAddress), 0,
398                     ipv4prefixes.stream()
399                             .map(IpPrefix::new)
400                             .collect(Collectors.toList()));
401             final Update update1 = PeerUtil.createUpdate(peerRole, Collections.emptyList(), 100, reachIpv4, null);
402             waitFutureSuccess(session.writeAndFlush(update1));
403         }
404
405         if (ipv6prefixes != null && !ipv4prefixes.isEmpty()) {
406             final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddress(ipv6NeighborAddress), 0,
407                     ipv6prefixes.stream()
408                             .map(IpPrefix::new)
409                             .collect(Collectors.toList()));
410             final Update update2 = PeerUtil.createUpdate(peerRole, Collections.emptyList(), 100, reachIpv6, null);
411             waitFutureSuccess(session.writeAndFlush(update2));
412         }
413     }
414
415     private static Open createClassicOpen(boolean addGraceful) {
416         final Map<TablesKey, Boolean> graceful = new HashMap<>();
417         if (addGraceful) {
418             graceful.put(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class), true);
419         }
420         return new OpenBuilder()
421                 .setMyAsNumber((int) AS)
422                 .setHoldTimer(HOLDTIMER)
423                 .setVersion(new ProtocolVersion((short) 4))
424                 .setBgpParameters(Collections.singletonList(createParameter(false, true, graceful)))
425                 .setBgpIdentifier(PEER1)
426                 .build();
427     }
428 }