Bump MDSAL to 4.0.0
[bgpcep.git] / bgp / parser-spi / src / main / java / org / opendaylight / protocol / bgp / parser / spi / pojo / SimpleNlriRegistry.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.spi.pojo;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.Iterables;
14 import io.netty.buffer.ByteBuf;
15 import io.netty.buffer.Unpooled;
16 import java.util.AbstractMap.SimpleEntry;
17 import java.util.Map.Entry;
18 import java.util.Optional;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import org.opendaylight.bgp.concepts.NextHopUtil;
22 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
23 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
24 import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
25 import org.opendaylight.protocol.bgp.parser.spi.NextHopParserSerializer;
26 import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
27 import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
28 import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
29 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
30 import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.next.hop.CNextHop;
40 import org.opendaylight.yangtools.concepts.AbstractRegistration;
41 import org.opendaylight.yangtools.concepts.Registration;
42 import org.opendaylight.yangtools.yang.binding.DataObject;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 final class SimpleNlriRegistry implements NlriRegistry {
47
48     private static final int RESERVED = 1;
49     private static final int NEXT_HOP_LENGHT = 1;
50     private static final String PARSER_NOT_FOUND = "Nlri parser not found for table type {}";
51     private static final Logger LOG = LoggerFactory.getLogger(SimpleNlriRegistry.class);
52
53     private final ConcurrentMap<BgpTableType, NlriParser> handlers = new ConcurrentHashMap<>();
54     private final ConcurrentMap<Class<? extends DataObject>, NlriSerializer> serializers = new ConcurrentHashMap<>();
55     private final ConcurrentMap<BgpTableType, NextHopParserSerializer> nextHopParsers = new ConcurrentHashMap<>();
56     private final ConcurrentMap<Entry<Class<? extends CNextHop>, BgpTableType>,
57             NextHopParserSerializer> nextHopSerializers = new ConcurrentHashMap<>();
58     private final SubsequentAddressFamilyRegistry safiReg;
59     private final AddressFamilyRegistry afiReg;
60
61     SimpleNlriRegistry(final AddressFamilyRegistry afiReg, final SubsequentAddressFamilyRegistry safiReg) {
62         this.afiReg = requireNonNull(afiReg);
63         this.safiReg = requireNonNull(safiReg);
64     }
65
66     private static BgpTableType createKey(final Class<? extends AddressFamily> afi,
67         final Class<? extends SubsequentAddressFamily> safi) {
68         requireNonNull(afi);
69         requireNonNull(safi);
70         return new BgpTableTypeImpl(afi, safi);
71     }
72
73     synchronized Registration registerNlriSerializer(final Class<? extends DataObject> nlriClass,
74             final NlriSerializer serializer) {
75         final NlriSerializer prev = this.serializers.get(nlriClass);
76         checkState(prev == null, "Serializer already bound to class " + prev);
77
78         this.serializers.put(nlriClass, serializer);
79
80         final Object lock = this;
81         return new AbstractRegistration() {
82             @Override
83             protected void removeRegistration() {
84                 synchronized (lock) {
85                     SimpleNlriRegistry.this.serializers.remove(nlriClass);
86                 }
87             }
88         };
89     }
90
91     synchronized Registration registerNlriParser(final Class<? extends AddressFamily> afi,
92         final Class<? extends SubsequentAddressFamily> safi, final NlriParser parser,
93         final NextHopParserSerializer nextHopSerializer, final Class<? extends CNextHop> cnextHopClass,
94         final Class<? extends CNextHop>... cnextHopClassList) {
95         final BgpTableType key = createKey(afi, safi);
96         final NlriParser prev = this.handlers.get(key);
97         checkState(prev == null, "AFI/SAFI is already bound to parser " + prev);
98
99         this.handlers.put(key, parser);
100         this.nextHopParsers.put(key,nextHopSerializer);
101
102         if (cnextHopClass != null) {
103             final Entry<Class<? extends CNextHop>, BgpTableType> nhKey = new SimpleEntry<>(cnextHopClass, key);
104             this.nextHopSerializers.put(nhKey, nextHopSerializer);
105             for (final Class<? extends CNextHop> cnextHop : cnextHopClassList) {
106                 final Entry<Class<? extends CNextHop>, BgpTableType> nhKeys = new SimpleEntry<>(cnextHop, key);
107                 this.nextHopSerializers.put(nhKeys, nextHopSerializer);
108             }
109         }
110
111         final Object lock = this;
112         return new AbstractRegistration() {
113             @Override
114             protected void removeRegistration() {
115                 synchronized (lock) {
116                     SimpleNlriRegistry.this.handlers.remove(key);
117                     SimpleNlriRegistry.this.nextHopParsers.remove(key);
118                     if (cnextHopClass != null) {
119                         final Entry<Class<? extends CNextHop>, BgpTableType> nhKey
120                                 = new SimpleEntry<>(cnextHopClass, key);
121                         SimpleNlriRegistry.this.nextHopSerializers.remove(nhKey);
122                         for (final Class<? extends CNextHop> cnextHop : cnextHopClassList) {
123                             final Entry<Class<? extends CNextHop>, BgpTableType> nhKeys
124                                     = new SimpleEntry<>(cnextHop, key);
125                             SimpleNlriRegistry.this.nextHopSerializers.remove(nhKeys);
126                         }
127                     }
128                 }
129             }
130         };
131     }
132
133     private Class<? extends AddressFamily> getAfi(final ByteBuf buffer) throws BGPParsingException {
134         final int afiVal = buffer.readUnsignedShort();
135         final Class<? extends AddressFamily> afi = this.afiReg.classForFamily(afiVal);
136         if (afi == null) {
137             throw new BGPParsingException("Address Family Identifier: '" + afiVal + "' not supported.");
138         }
139         return afi;
140     }
141
142     private Class<? extends SubsequentAddressFamily> getSafi(final ByteBuf buffer) throws BGPParsingException {
143         final int safiVal = buffer.readUnsignedByte();
144         final Class<? extends SubsequentAddressFamily> safi = this.safiReg.classForFamily(safiVal);
145         if (safi == null) {
146             throw new BGPParsingException("Subsequent Address Family Identifier: '" + safiVal + "' not supported.");
147         }
148         return safi;
149     }
150
151     @Override
152     public MpUnreachNlri parseMpUnreach(final ByteBuf buffer, final PeerSpecificParserConstraint constraint)
153             throws BGPParsingException {
154         final MpUnreachNlriBuilder builder = new MpUnreachNlriBuilder();
155         builder.setAfi(getAfi(buffer));
156         builder.setSafi(getSafi(buffer));
157
158         if (buffer.isReadable()) {
159             final ByteBuf nlri = buffer.slice();
160             final BgpTableType key = createKey(builder.getAfi(), builder.getSafi());
161             final NlriParser parser = this.handlers.get(key);
162             if (parser == null) {
163                 LOG.warn(PARSER_NOT_FOUND, key);
164             } else {
165                 parser.parseNlri(nlri, builder, constraint);
166             }
167         }
168         return builder.build();
169     }
170
171     @Override
172     public void serializeMpReach(final MpReachNlri mpReachNlri, final ByteBuf byteAggregator) {
173         final Class<? extends AddressFamily> afi = mpReachNlri.getAfi();
174         final Class<? extends SubsequentAddressFamily> safi = mpReachNlri.getSafi();
175         byteAggregator.writeShort(this.afiReg.numberForClass(afi));
176         byteAggregator.writeByte(this.safiReg.numberForClass(safi));
177
178         final CNextHop cNextHop = mpReachNlri.getCNextHop();
179         if (cNextHop != null) {
180             final Entry<Class<? extends CNextHop>, BgpTableType> key = new SimpleEntry(
181                     cNextHop.implementedInterface(), new BgpTableTypeImpl(afi, safi));
182             final NextHopParserSerializer nextHopSerializer = this.nextHopSerializers.get(key);
183             final ByteBuf nextHopBuffer = Unpooled.buffer();
184             nextHopSerializer.serializeNextHop(cNextHop, nextHopBuffer);
185             byteAggregator.writeByte(nextHopBuffer.writerIndex());
186             byteAggregator.writeBytes(nextHopBuffer);
187
188         } else {
189             byteAggregator.writeZero(NEXT_HOP_LENGHT);
190         }
191         byteAggregator.writeZero(RESERVED);
192     }
193
194     @Override
195     public void serializeMpUnReach(final MpUnreachNlri mpUnreachNlri, final ByteBuf byteAggregator) {
196         byteAggregator.writeShort(this.afiReg.numberForClass(mpUnreachNlri.getAfi()));
197         byteAggregator.writeByte(this.safiReg.numberForClass(mpUnreachNlri.getSafi()));
198     }
199
200     @Override
201     public Iterable<NlriSerializer> getSerializers() {
202         return Iterables.unmodifiableIterable(this.serializers.values());
203     }
204
205     @Override
206     public MpReachNlri parseMpReach(final ByteBuf buffer, final PeerSpecificParserConstraint constraint)
207             throws BGPParsingException {
208         final MpReachNlriBuilder builder = new MpReachNlriBuilder();
209         final Class<? extends AddressFamily> afi = getAfi(buffer);
210         final Class<? extends SubsequentAddressFamily> safi = getSafi(buffer);
211         builder.setAfi(afi);
212         builder.setSafi(safi);
213
214         final BgpTableType key = createKey(builder.getAfi(), builder.getSafi());
215
216         final int nextHopLength = buffer.readUnsignedByte();
217         if (nextHopLength != 0) {
218             final NextHopParserSerializer nextHopParser = this.nextHopParsers.get(key);
219             if (nextHopParser != null) {
220                 builder.setCNextHop(nextHopParser.parseNextHop(buffer.readSlice(nextHopLength)));
221             } else {
222                 builder.setCNextHop(NextHopUtil.parseNextHop(buffer.readSlice(nextHopLength)));
223                 LOG.warn("NexHop Parser/Serializer for AFI/SAFI ({},{}) not bound",afi,safi);
224             }
225         }
226         buffer.skipBytes(RESERVED);
227
228         final ByteBuf nlri = buffer.slice();
229         final NlriParser parser = this.handlers.get(key);
230         if (parser == null) {
231             LOG.warn(PARSER_NOT_FOUND, key);
232         } else {
233             parser.parseNlri(nlri, builder, constraint);
234         }
235         return builder.build();
236     }
237
238     @Override
239     public Optional<MpUnreachNlri> convertMpReachToMpUnReach(final MpReachNlri mpReachNlri,
240             final MpUnreachNlri mpUnreachNlri) {
241         if (mpUnreachNlri == null) {
242             return Optional.of(new MpUnreachNlriBuilder()
243                     .setWithdrawnRoutes(new WithdrawnRoutesBuilder()
244                             .setDestinationType(mpReachNlri.getAdvertizedRoutes().getDestinationType())
245                             .build())
246                     .build());
247         }
248
249         final BgpTableType key = createKey(mpUnreachNlri.getAfi(), mpUnreachNlri.getSafi());
250         final NlriParser parser = this.handlers.get(key);
251         if (parser == null) {
252             LOG.debug("Parser for {} not found", key);
253             return Optional.empty();
254         }
255
256         final MpUnreachNlriBuilder builder = new MpUnreachNlriBuilder(mpUnreachNlri);
257         return parser.convertMpReachToMpUnReach(mpReachNlri, builder) ? Optional.of(builder.build()) : Optional.empty();
258     }
259 }