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