Update MRI projects for Aluminium
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / message / BGPOpenMessageParser.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.message;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static com.google.common.base.Verify.verify;
13 import static com.google.common.base.Verify.verifyNotNull;
14 import static java.util.Objects.requireNonNull;
15
16 import com.google.common.collect.ImmutableList;
17 import io.netty.buffer.ByteBuf;
18 import io.netty.buffer.ByteBufUtil;
19 import io.netty.buffer.Unpooled;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Optional;
23 import java.util.OptionalInt;
24 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
25 import org.opendaylight.protocol.bgp.parser.BGPError;
26 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
27 import org.opendaylight.protocol.bgp.parser.spi.MessageParser;
28 import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer;
29 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
30 import org.opendaylight.protocol.bgp.parser.spi.ParameterLengthOverflowException;
31 import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
32 import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
33 import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
34 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
35 import org.opendaylight.protocol.util.Ipv4Util;
36 import org.opendaylight.protocol.util.Values;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.Open;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.OpenBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.open.message.BgpParameters;
42 import org.opendaylight.yangtools.yang.binding.Notification;
43 import org.opendaylight.yangtools.yang.common.Uint16;
44 import org.opendaylight.yangtools.yang.common.Uint32;
45 import org.opendaylight.yangtools.yang.common.netty.ByteBufUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * Parser for BGP Open message.
51  */
52 public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
53
54     private static final Logger LOG = LoggerFactory.getLogger(BGPOpenMessageParser.class);
55
56     public static final int TYPE = 1;
57
58     private static final int VERSION_SIZE = 1;
59     private static final int AS_SIZE = 2;
60     private static final int HOLD_TIME_SIZE = 2;
61     private static final int BGP_ID_SIZE = 4;
62     private static final int OPT_PARAM_LENGTH_SIZE = 1;
63
64     // Optional Parameter Type for Extended Optional Parameters Length
65     private static final int OPT_PARAM_EXT_PARAM = 255;
66
67     private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE
68             + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
69
70     private static final int BGP_VERSION = 4;
71
72     public static final int AS_TRANS = 23456;
73
74     private final ParameterRegistry reg;
75
76     public BGPOpenMessageParser(final ParameterRegistry reg) {
77         this.reg = requireNonNull(reg);
78     }
79
80     /**
81      * Serializes given BGP Open message to byte array, without the header.
82      *
83      * @param msg BGP Open message to be serialized.
84      * @param bytes ByteBuf where the message will be serialized
85      */
86     @Override
87     public void serializeMessage(final Notification msg, final ByteBuf bytes) {
88         checkArgument(msg instanceof Open, "Message needs to be of type Open, not %s", msg);
89         final Open open = (Open) msg;
90         final ByteBuf msgBody = Unpooled.buffer()
91                 .writeByte(BGP_VERSION);
92
93         // When our AS number does not fit into two bytes, we report it as AS_TRANS
94         int openAS = open.getMyAsNumber().toJava();
95         if (openAS > Values.UNSIGNED_SHORT_MAX_VALUE) {
96             openAS = AS_TRANS;
97         }
98         msgBody
99             .writeShort(openAS)
100             .writeShort(open.getHoldTimer().toJava())
101             .writeBytes(Ipv4Util.bytesForAddress(open.getBgpIdentifier()));
102
103         serializeParameters(open.getBgpParameters(), msgBody);
104
105         MessageUtil.formatMessage(TYPE, msgBody, bytes);
106     }
107
108     private void serializeParameters(final List<BgpParameters> params, final ByteBuf msgBody) {
109         if (params == null || params.isEmpty()) {
110             msgBody.writeByte(0);
111             return;
112         }
113
114         final ByteBuf normal = normalSerializeParameters(params);
115         if (normal != null) {
116             final int length = normal.writerIndex();
117             verify(length <= Values.UNSIGNED_BYTE_MAX_VALUE);
118             msgBody.writeByte(length);
119             msgBody.writeBytes(normal);
120             return;
121         }
122
123         final ByteBuf buffer = Unpooled.buffer();
124         for (final BgpParameters param : params) {
125             final Optional<ParameterSerializer> optSer = reg.findSerializer(param);
126             if (optSer.isPresent()) {
127                 optSer.get().serializeExtendedParameter(param, buffer);
128             } else {
129                 LOG.debug("Ignoring unregistered parameter {}", param);
130             }
131         }
132
133         final int length = buffer.writerIndex();
134         checkState(length <= Values.UNSIGNED_SHORT_MAX_VALUE);
135
136         // The non-extended Optional Parameters Length field MUST be set to 255
137         msgBody.writeByte(Values.UNSIGNED_BYTE_MAX_VALUE);
138         // The subsequent one-octet field, that in the non-extended format would
139         // be the first Optional Parameter Type field, MUST be set to 255
140         msgBody.writeByte(OPT_PARAM_EXT_PARAM);
141         // Extended Optional Parameters Length
142         msgBody.writeShort(length);
143         msgBody.writeBytes(buffer);
144     }
145
146     private ByteBuf normalSerializeParameters(final List<BgpParameters> params) {
147         final ByteBuf buffer = Unpooled.buffer();
148         for (final BgpParameters param : params) {
149             final Optional<ParameterSerializer> optSer = reg.findSerializer(param);
150             if (optSer.isPresent()) {
151                 try {
152                     optSer.get().serializeParameter(param, buffer);
153                 } catch (ParameterLengthOverflowException e) {
154                     LOG.debug("Forcing extended parameter serialization", e);
155                     return null;
156                 }
157             } else {
158                 LOG.debug("Ingnoring unregistered parameter {}", param);
159             }
160         }
161
162         final int length = buffer.writerIndex();
163         if (length > Values.UNSIGNED_BYTE_MAX_VALUE) {
164             LOG.debug("Final parameter size is {}, forcing extended serialization", length);
165             return null;
166         }
167
168         return buffer;
169     }
170
171     /**
172      * Parses given byte array to BGP Open message.
173      *
174      * @param body byte array representing BGP Open message, without header
175      * @param messageLength the length of the message
176      * @return {@link Open} BGP Open Message
177      * @throws BGPDocumentedException if the parsing was unsuccessful
178      */
179     @Override
180     public Open parseMessageBody(final ByteBuf body, final int messageLength,
181             final PeerSpecificParserConstraint constraint) throws BGPDocumentedException {
182         checkArgument(body != null, "Buffer cannot be null.");
183
184         if (body.readableBytes() < MIN_MSG_LENGTH) {
185             throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
186         }
187         final int version = body.readUnsignedByte();
188         if (version != BGP_VERSION) {
189             throw new BGPDocumentedException("BGP Protocol version " + version + " not supported.",
190                     BGPError.VERSION_NOT_SUPPORTED);
191         }
192         final AsNumber as = new AsNumber(Uint32.valueOf(body.readUnsignedShort()));
193         final Uint16 holdTime = ByteBufUtils.readUint16(body);
194         if (Uint16.ONE.equals(holdTime) || Uint16.TWO.equals(holdTime)) {
195             throw new BGPDocumentedException("Hold time value not acceptable.", BGPError.HOLD_TIME_NOT_ACC);
196         }
197         final Ipv4AddressNoZone bgpId;
198         try {
199             bgpId = Ipv4Util.addressForByteBuf(body);
200         } catch (final IllegalArgumentException e) {
201             throw new BGPDocumentedException("BGP Identifier is not a valid IPv4 Address", BGPError.BAD_BGP_ID, e);
202         }
203         final int optLength = body.readUnsignedByte();
204         final List<BgpParameters> optParams = parseParameters(body.slice(), optLength);
205         LOG.debug("BGP Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as,
206                 holdTime, bgpId, optParams);
207         return new OpenBuilder().setMyAsNumber(Uint16.valueOf(as.getValue())).setHoldTimer(holdTime)
208                 .setBgpIdentifier(bgpId).setBgpParameters(optParams).build();
209     }
210
211     private List<BgpParameters> parseParameters(final ByteBuf buffer, final int length) throws BGPDocumentedException {
212         if (length == 0) {
213             return ImmutableList.of();
214         }
215         if (LOG.isTraceEnabled()) {
216             LOG.trace("Started parsing of BGP parameter: {} length {}", ByteBufUtil.hexDump(buffer), length);
217         }
218
219         final int realLength;
220         final OptionalInt extendedLength = extractExtendedLength(buffer, length);
221         if (extendedLength.isPresent()) {
222             realLength = extendedLength.getAsInt();
223             if (realLength < Values.UNSIGNED_BYTE_MAX_VALUE) {
224                 LOG.debug("Peer used Extended Optional Parameters Length to encode length {}", realLength);
225             }
226         } else {
227             realLength = length;
228         }
229
230         // We have determined the real length, we can trim the buffer now
231         if (buffer.readableBytes() > realLength) {
232             buffer.writerIndex(buffer.readerIndex() + realLength);
233             LOG.trace("Truncated BGP parameter buffer to length {}: {}", realLength, ByteBufUtil.hexDump(buffer));
234         }
235
236         final int lengthSize = extendedLength.isPresent() ? 1 : 2;
237         final List<BgpParameters> params = new ArrayList<>();
238         while (buffer.isReadable()) {
239             final int paramType = buffer.readUnsignedByte();
240             final Optional<ParameterParser> parser = reg.findParser(paramType);
241             if (!parser.isPresent()) {
242                 throw new BGPDocumentedException("Parameter " + paramType + " not supported",
243                     BGPError.OPT_PARAM_NOT_SUPPORTED);
244             }
245             if (buffer.readableBytes() <= lengthSize) {
246                 throw new BGPDocumentedException("Malformed parameter encountered (" + buffer.readableBytes()
247                         + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
248             }
249             final int paramLength = extendedLength.isPresent() ? buffer.readUnsignedShort() : buffer.readUnsignedByte();
250             final ByteBuf paramBody = buffer.readSlice(paramLength);
251
252             final BgpParameters param;
253             try {
254                 param = parser.get().parseParameter(paramBody);
255             } catch (final BGPParsingException e) {
256                 throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
257             }
258
259             params.add(verifyNotNull(param));
260         }
261
262         LOG.trace("Parsed BGP parameters: {}", params);
263         return params;
264     }
265
266     private static OptionalInt extractExtendedLength(final ByteBuf buffer, final int length)
267             throws BGPDocumentedException {
268         final int type = buffer.markReaderIndex().readUnsignedByte();
269         if (type != OPT_PARAM_EXT_PARAM) {
270             // Not extended length
271             buffer.resetReaderIndex();
272             return OptionalInt.empty();
273         }
274         if (length != Values.UNSIGNED_BYTE_MAX_VALUE) {
275             LOG.debug("Peer uses Extended Optional Parameters Length, but indicated RFC4271 length as {}", length);
276         }
277         if (length < 3) {
278             throw new BGPDocumentedException("Malformed Extended Length parameter encountered ("
279                     + (length - 1) + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
280         }
281         final int avail = buffer.readableBytes();
282         if (avail < 2) {
283             throw new BGPDocumentedException("Buffer underrun: require 2 bytes, only " + avail + " bytes left",
284                 BGPError.UNSPECIFIC_OPEN_ERROR);
285         }
286
287         return OptionalInt.of(buffer.readUnsignedShort());
288     }
289 }