Rework ListenerRegistration to be more usable
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / BGPUpdateEventBuilder.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  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.parser.impl;
9
10 import java.util.Collection;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.Set;
16
17 import javax.annotation.concurrent.NotThreadSafe;
18
19 import org.opendaylight.protocol.bgp.concepts.BGPObject;
20 import org.opendaylight.protocol.bgp.concepts.BaseBGPObjectState;
21 import org.opendaylight.protocol.bgp.linkstate.IPv4PrefixIdentifier;
22 import org.opendaylight.protocol.bgp.linkstate.IPv6PrefixIdentifier;
23 import org.opendaylight.protocol.bgp.linkstate.LinkIdentifier;
24 import org.opendaylight.protocol.bgp.linkstate.NetworkLinkImpl;
25 import org.opendaylight.protocol.bgp.linkstate.NetworkNodeImpl;
26 import org.opendaylight.protocol.bgp.linkstate.NetworkObjectState;
27 import org.opendaylight.protocol.bgp.linkstate.NetworkPrefixState;
28 import org.opendaylight.protocol.bgp.linkstate.NetworkRouteState;
29 import org.opendaylight.protocol.bgp.linkstate.NodeIdentifier;
30 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
31 import org.opendaylight.protocol.bgp.parser.BGPUpdateEvent;
32 import org.opendaylight.protocol.bgp.parser.impl.message.BGPUpdateMessageParser;
33 import org.opendaylight.protocol.bgp.parser.impl.message.update.LinkStateParser;
34 import org.opendaylight.protocol.bgp.util.BGPIPv4PrefixImpl;
35 import org.opendaylight.protocol.bgp.util.BGPIPv4RouteImpl;
36 import org.opendaylight.protocol.bgp.util.BGPIPv6PrefixImpl;
37 import org.opendaylight.protocol.bgp.util.BGPIPv6RouteImpl;
38 import org.opendaylight.protocol.bgp.util.BGPLinkImpl;
39 import org.opendaylight.protocol.bgp.util.BGPNodeImpl;
40 import org.opendaylight.protocol.concepts.IPv4Address;
41 import org.opendaylight.protocol.concepts.IPv6Address;
42 import org.opendaylight.protocol.concepts.Prefix;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AsPathSegment;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpAggregator;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.Community;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.extended.community.ExtendedCommunity;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.CIpv4NextHop;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.CIpv6NextHop;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.common.collect.Lists;
54 import com.google.common.collect.Maps;
55 import com.google.common.collect.Sets;
56
57 /**
58  * 
59  * Builds BGPUpdateEvent. This code was originally in {@link BGPUpdateMessageParser}. Moved here during refactoring.
60  * 
61  * Withdrawn routes or nlri that contain directly prefixes, can contain only IPv4 Prefixes.
62  */
63 @NotThreadSafe
64 public class BGPUpdateEventBuilder {
65
66         private static final Logger log = LoggerFactory.getLogger(BGPUpdateEventBuilder.class);
67
68         /**
69          * 
70          * Length of the withdrawn_routes field, in bytes.
71          */
72
73         private int withdrawnRoutesLength;
74
75         /**
76          * 
77          * List of IP address prefixes for the routes that are being withdrawn. Can be empty when there are no routes to
78          * 
79          * withdraw.
80          */
81
82         private Set<Prefix<IPv4Address>> withdrawnRoutes;
83
84         /**
85          * 
86          * Length of the total_path_attributes field, in bytes.
87          */
88
89         private int totalPathAttrLength;
90
91         /**
92          * 
93          * List of path attributes. Can be empty when there are only withdrawn routes present.
94          */
95
96         private List<PathAttribute> pathAttributes;
97
98         /**
99          * 
100          * List of IP address prefixes of routes that are advertised.
101          */
102
103         private Set<Prefix<IPv4Address>> nlri;
104
105         /**
106          * 
107          * Fills in BGP Objects that need to be added to topology. The method first checks and sets Path Attributes. If the
108          * 
109          * NLRI field is not empty, create for each of the prefixes present in NLRI, a BGPRoute with the attributes and
110          * 
111          * corresponding Prefix. If MP_REACH_NLRI attribute is found, check its NLRI, if its not of type Link State, do the
112          * 
113          * same.
114          * 
115          * 
116          * 
117          * @param pathAttributes
118          * 
119          * @param nlri
120          * 
121          * 
122          * 
123          * @return set of BGP Objects that need to be added
124          * 
125          * @throws BGPParsingException
126          */
127
128         private Set<BGPObject> fillAddedObjects(final List<PathAttribute> pathAttributes, final Set<Prefix<IPv4Address>> nlri)
129                         throws BGPParsingException {
130                 BgpOrigin origin = null;
131                 final List<AsPathSegment> aspath = Lists.newArrayList();
132                 CIpv4NextHop nextHop = null;
133                 BgpAggregator aggregator = null;
134                 final Set<ExtendedCommunity> ecomm = Sets.newHashSet();
135                 final Set<Community> comm = Sets.newHashSet();
136                 final Map<Integer, ByteList> linkstate = Maps.newHashMap();
137                 for (final PathAttribute pa : pathAttributes) {
138                         if (pa.getValue() instanceof BgpOrigin) {
139                                 origin = (BgpOrigin) pa.getValue();
140                         } else if (pa.getValue() instanceof List) {
141                                 for (final Object o : (List<?>) pa.getValue()) {
142                                         if (o instanceof AsPathSegment) {
143                                                 aspath.add((AsPathSegment) o);
144                                         }
145                                 }
146                         } else if (pa.getValue() instanceof CIpv4NextHop) {
147                                 nextHop = (CIpv4NextHop) pa.getValue();
148                         } else if (pa.getValue() instanceof BgpAggregator) {
149                                 aggregator = (BgpAggregator) pa.getValue();
150                         } else if (pa.getValue() instanceof Set) {
151                                 for (final Object o : (Set<?>) pa.getValue()) {
152                                         if (o instanceof ExtendedCommunity) {
153                                                 ecomm.add((ExtendedCommunity) o);
154                                         } else if (o instanceof Community) {
155                                                 comm.add((Community) o);
156                                         }
157                                 }
158                         } else if (pa.getValue() instanceof Map) {
159                                 for (final Entry<?, ?> entry : ((Map<?, ?>) pa.getValue()).entrySet()) {
160                                         if (entry.getValue() instanceof ByteList) {
161                                                 final ByteList lb = (ByteList) entry.getValue();
162                                                 linkstate.put((Integer) entry.getKey(), lb);
163                                         }
164                                 }
165                         }
166                 }
167
168                 final BaseBGPObjectState base = new BaseBGPObjectState(origin, aggregator);
169                 final NetworkObjectState nos = new NetworkObjectState(aspath, comm, ecomm);
170                 final Set<BGPObject> added = new HashSet<BGPObject>();
171                 if (!nlri.isEmpty()) {
172                         final NetworkRouteState nrs = new NetworkRouteState(nos, nextHop);
173                         for (final Prefix<IPv4Address> p : nlri) {
174                                 added.add(new BGPIPv4RouteImpl(p, base, nrs));
175                         }
176                 }
177
178                 final MPReach<?> mpreach = findMP(pathAttributes, true);
179                 if (mpreach != null) {
180                         if (mpreach instanceof IPv4MP) {
181                                 final IPv4MP ipv4mp = (IPv4MP) mpreach;
182                                 final CIpv4NextHop v4nextHop = ipv4mp.getNextHop();
183                                 final NetworkRouteState nrs = new NetworkRouteState(nos, v4nextHop);
184                                 for (final Prefix<IPv4Address> p : ipv4mp.getNlri()) {
185                                         added.add(new BGPIPv4RouteImpl(p, base, nrs));
186                                 }
187                         } else if (mpreach instanceof IPv6MP) {
188                                 final IPv6MP ipv6mp = (IPv6MP) mpreach;
189                                 final CIpv6NextHop v6nextHop = ipv6mp.getNextHop();
190                                 final NetworkRouteState nrs = new NetworkRouteState(nos, v6nextHop);
191                                 for (final Prefix<IPv6Address> p : ipv6mp.getNlri()) {
192                                         added.add(new BGPIPv6RouteImpl(p, base, nrs));
193                                 }
194                         } else if (mpreach instanceof BGPNodeMP) {
195                                 final Set<NodeIdentifier> nodes = ((BGPNodeMP) mpreach).getNlri();
196                                 if (!LinkStateParser.verifyNode(linkstate.keySet()))
197                                         throw new BGPParsingException("Some attributes from LINK_STATE Path attribute don't belong to advertised node.");
198                                 for (final NodeIdentifier desc : nodes) {
199                                         final NetworkNodeImpl n = LinkStateParser.parseNodeAttributes(desc, linkstate);
200                                         n.setASPath(aspath);
201                                         n.setExtendedCommunities(ecomm);
202                                         n.setCommunities(comm);
203                                         final BGPNodeImpl bgpNode = new BGPNodeImpl(base, desc, n.currentState());
204                                         log.debug("Adding bgp node {}", bgpNode);
205                                         added.add(bgpNode);
206                                 }
207                         } else if (mpreach instanceof BGPLinkMP) {
208                                 final Set<LinkIdentifier> links = ((BGPLinkMP) mpreach).getNlri();
209                                 if (!LinkStateParser.verifyLink(linkstate.keySet()))
210                                         throw new BGPParsingException("Some attributes from LINK_STATE Path attribute don't belong to advertised link.");
211                                 for (final LinkIdentifier desc : links) {
212                                         final NetworkLinkImpl l = LinkStateParser.parseLinkAttributes(desc, linkstate);
213                                         l.setASPath(aspath);
214                                         l.setExtendedCommunities(ecomm);
215                                         l.setCommunities(comm);
216                                         log.debug("Adding bgp link {}", l);
217                                         added.add(new BGPLinkImpl(base, desc, l.currentState()));
218                                 }
219                         } else if (mpreach instanceof BGPIPv4PrefixMP) {
220                                 final Set<IPv4PrefixIdentifier> prefixes = ((BGPIPv4PrefixMP) mpreach).getNlri();
221                                 if (!LinkStateParser.verifyPrefix(linkstate.keySet()))
222                                         throw new BGPParsingException("Some attributes from LINK_STATE Path attribute don't belong to advertised prefix.");
223                                 final NetworkPrefixState nps = LinkStateParser.parsePrefixAttributes(((BGPIPv4PrefixMP) mpreach).getSourceProtocol(), nos,
224                                                 linkstate);
225                                 for (final IPv4PrefixIdentifier desc : prefixes) {
226                                         log.debug("Adding IPv4 Prefix {} State {}", desc, nps);
227                                         added.add(new BGPIPv4PrefixImpl(base, desc, nps));
228                                 }
229                         } else if (mpreach instanceof BGPIPv6PrefixMP) {
230                                 final Set<IPv6PrefixIdentifier> prefixes = ((BGPIPv6PrefixMP) mpreach).getNlri();
231                                 if (!LinkStateParser.verifyPrefix(linkstate.keySet()))
232                                         throw new BGPParsingException("Some attributes from LINK_STATE Path attribute don't belong to advertised prefix.");
233
234                                 final NetworkPrefixState nps = LinkStateParser.parsePrefixAttributes(((BGPIPv6PrefixMP) mpreach).getSourceProtocol(), nos,
235                                                 linkstate);
236                                 for (final IPv6PrefixIdentifier desc : prefixes) {
237                                         log.debug("Adding IPv6 Prefix {} State {}", desc, nps);
238                                         added.add(new BGPIPv6PrefixImpl(base, desc, nps));
239                                 }
240                         }
241                 }
242                 return added;
243
244         }
245
246         /**
247          * Fills in Identifiers that need to be removed. First, check field withdrawn routes, that can contain only IPv4
248          * prefixes. Then, check the presence of MP_UNREACH_NLRI and if its NLRI contains Prefixes, add them to removed
249          * field. For link state information, Node & LinkIdentifiers are added to the Set.
250          * 
251          * @param pathAttributes
252          * @param withdrawnRoutes
253          * 
254          * @return set of identifiers that need to be removed
255          */
256
257         private Set<?> fillRemovedObjects(final List<PathAttribute> pathAttributes, final Set<Prefix<IPv4Address>> withdrawnRoutes) {
258                 final Set<Object> removed = Sets.newHashSet();
259                 if (!withdrawnRoutes.isEmpty()) {
260                         removed.addAll(withdrawnRoutes);
261                 }
262                 final MPReach<?> mpunreach = findMP(pathAttributes, false);
263                 if (mpunreach != null) {
264                         if (mpunreach instanceof IPv4MP) {
265                                 final IPv4MP ipv4mp = (IPv4MP) mpunreach;
266                                 if (!ipv4mp.getNlri().isEmpty()) {
267                                         removed.addAll(ipv4mp.getNlri());
268                                 }
269                         } else if (mpunreach instanceof IPv6MP) {
270                                 final IPv6MP ipv6mp = (IPv6MP) mpunreach;
271                                 if (!ipv6mp.getNlri().isEmpty()) {
272                                         removed.addAll(ipv6mp.getNlri());
273                                 }
274                         } else if (mpunreach instanceof BGPNodeMP) {
275                                 for (final NodeIdentifier node : ((BGPNodeMP) mpunreach).getNlri()) {
276                                         removed.add(node);
277                                 }
278                         } else if (mpunreach instanceof BGPLinkMP) {
279                                 for (final LinkIdentifier link : ((BGPLinkMP) mpunreach).getNlri()) {
280                                         removed.add(link);
281                                 }
282                         } else if (mpunreach instanceof BGPIPv4PrefixMP) {
283                                 for (final IPv4PrefixIdentifier pref : ((BGPIPv4PrefixMP) mpunreach).getNlri()) {
284                                         removed.add(pref);
285                                 }
286                         } else if (mpunreach instanceof BGPIPv6PrefixMP) {
287                                 for (final IPv6PrefixIdentifier pref : ((BGPIPv6PrefixMP) mpunreach).getNlri()) {
288                                         removed.add(pref);
289                                 }
290                         }
291                 }
292                 return removed;
293
294         }
295
296         /**
297          * Finds MPReach object in Path Attribute list (depending on reachability boolean) and returns typecasted object.
298          * 
299          * @param arrayList list of path attributes
300          * @param reachable true if we search for MP_REACH_NLRI, false if we search for MP_UNREACH_NLRI
301          * 
302          * @return cated MPReach object
303          */
304         private static <T> MPReach<?> findMP(final Collection<PathAttribute> arrayList, final boolean reachable) {
305                 for (final PathAttribute o : arrayList) {
306                         final Object v = o.getValue();
307                         if (v != null && v instanceof MPReach<?>) {
308                                 final MPReach<?> t = (MPReach<?>) v;
309                                 if (t.isReachable() == reachable)
310                                         return t;
311                         }
312                 }
313                 return null;
314         }
315
316         int getWithdrawnRoutesLength() {
317                 return this.withdrawnRoutesLength;
318         }
319
320         public void setWithdrawnRoutesLength(final int withdrawnRoutesLength) {
321                 this.withdrawnRoutesLength = withdrawnRoutesLength;
322         }
323
324         Set<Prefix<IPv4Address>> getWithdrawnRoutes() {
325                 return this.withdrawnRoutes;
326         }
327
328         public void setWithdrawnRoutes(final Set<Prefix<IPv4Address>> withdrawnRoutes) {
329                 this.withdrawnRoutes = withdrawnRoutes;
330         }
331
332         int getTotalPathAttrLength() {
333                 return this.totalPathAttrLength;
334         }
335
336         public void setTotalPathAttrLength(final int totalPathAttrLength) {
337                 this.totalPathAttrLength = totalPathAttrLength;
338         }
339
340         List<PathAttribute> getPathAttributes() {
341                 return this.pathAttributes;
342         }
343
344         public void setPathAttributes(final List<PathAttribute> pathAttributes) {
345                 this.pathAttributes = pathAttributes;
346         }
347
348         Set<Prefix<IPv4Address>> getNlri() {
349                 return this.nlri;
350         }
351
352         public void setNlri(final Set<Prefix<IPv4Address>> nlri) {
353                 this.nlri = nlri;
354         }
355
356         /**
357          * Builds BGP Update message.
358          * 
359          * @return BGP Update message
360          * @throws BGPParsingException
361          */
362         public BGPUpdateEvent buildEvent() throws BGPParsingException {
363                 final Set<BGPObject> added = fillAddedObjects(this.pathAttributes, this.nlri);
364                 final Set<?> removed = fillRemovedObjects(this.pathAttributes, this.withdrawnRoutes);
365                 return new BGPUpdateMessageImpl(added, removed);
366         }
367 }