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