2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.protocol.bgp.parser.impl.message;
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;
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;
49 * Parser for BGP Open message.
51 public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
53 private static final Logger LOG = LoggerFactory.getLogger(BGPOpenMessageParser.class);
55 public static final int TYPE = 1;
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;
63 // Optional Parameter Type for Extended Optional Parameters Length
64 private static final int OPT_PARAM_EXT_PARAM = 255;
66 private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE
67 + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
69 private static final int BGP_VERSION = 4;
71 public static final int AS_TRANS = 23456;
73 private final ParameterRegistry reg;
75 public BGPOpenMessageParser(final ParameterRegistry reg) {
76 this.reg = requireNonNull(reg);
80 * Serializes given BGP Open message to byte array, without the header.
82 * @param msg BGP Open message to be serialized.
83 * @param bytes ByteBuf where the message will be serialized
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);
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) {
99 .writeShort(open.getHoldTimer().toJava())
100 .writeBytes(Ipv4Util.bytesForAddress(open.getBgpIdentifier()));
102 serializeParameters(open.getBgpParameters(), msgBody);
104 MessageUtil.formatMessage(TYPE, msgBody, bytes);
107 private void serializeParameters(final List<BgpParameters> params, final ByteBuf msgBody) {
108 if (params == null || params.isEmpty()) {
109 msgBody.writeByte(0);
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);
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);
128 LOG.debug("Ignoring unregistered parameter {}", param);
132 final int length = buffer.writerIndex();
133 checkState(length <= Values.UNSIGNED_SHORT_MAX_VALUE);
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);
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()) {
151 optSer.get().serializeParameter(param, buffer);
152 } catch (ParameterLengthOverflowException e) {
153 LOG.debug("Forcing extended parameter serialization", e);
157 LOG.debug("Ingnoring unregistered parameter {}", param);
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);
171 * Parses given byte array to BGP Open message.
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
179 public Open parseMessageBody(final ByteBuf body, final int messageLength,
180 final PeerSpecificParserConstraint constraint) throws BGPDocumentedException {
181 checkArgument(body != null, "Buffer cannot be null.");
183 if (body.readableBytes() < MIN_MSG_LENGTH) {
184 throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
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);
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);
196 final Ipv4AddressNoZone bgpId;
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);
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();
210 private List<BgpParameters> parseParameters(final ByteBuf buffer, final int length) throws BGPDocumentedException {
212 return ImmutableList.of();
214 if (LOG.isTraceEnabled()) {
215 LOG.trace("Started parsing of BGP parameter: {} length {}", ByteBufUtil.hexDump(buffer), length);
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);
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));
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);
244 if (buffer.readableBytes() <= lengthSize) {
245 throw new BGPDocumentedException("Malformed parameter encountered (" + buffer.readableBytes()
246 + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
248 final int paramLength = extendedLength.isPresent() ? buffer.readUnsignedShort() : buffer.readUnsignedByte();
249 final ByteBuf paramBody = buffer.readSlice(paramLength);
251 final BgpParameters param;
253 param = parser.get().parseParameter(paramBody);
254 } catch (final BGPParsingException e) {
255 throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
258 params.add(verifyNotNull(param));
261 LOG.trace("Parsed BGP parameters: {}", params);
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();
273 if (length != Values.UNSIGNED_BYTE_MAX_VALUE) {
274 LOG.debug("Peer uses Extended Optional Parameters Length, but indicated RFC4271 length as {}", length);
277 throw new BGPDocumentedException("Malformed Extended Length parameter encountered ("
278 + (length - 1) + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
280 final int avail = buffer.readableBytes();
282 throw new BGPDocumentedException("Buffer underrun: require 2 bytes, only " + avail + " bytes left",
283 BGPError.UNSPECIFIC_OPEN_ERROR);
286 return OptionalInt.of(buffer.readUnsignedShort());