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.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;
50 * Parser for BGP Open message.
52 public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
54 private static final Logger LOG = LoggerFactory.getLogger(BGPOpenMessageParser.class);
56 public static final int TYPE = 1;
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;
64 // Optional Parameter Type for Extended Optional Parameters Length
65 private static final int OPT_PARAM_EXT_PARAM = 255;
67 private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE
68 + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
70 private static final int BGP_VERSION = 4;
72 public static final int AS_TRANS = 23456;
74 private final ParameterRegistry reg;
76 public BGPOpenMessageParser(final ParameterRegistry reg) {
77 this.reg = requireNonNull(reg);
81 * Serializes given BGP Open message to byte array, without the header.
83 * @param msg BGP Open message to be serialized.
84 * @param bytes ByteBuf where the message will be serialized
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);
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) {
100 .writeShort(open.getHoldTimer().toJava())
101 .writeBytes(Ipv4Util.bytesForAddress(open.getBgpIdentifier()));
103 serializeParameters(open.getBgpParameters(), msgBody);
105 MessageUtil.formatMessage(TYPE, msgBody, bytes);
108 private void serializeParameters(final List<BgpParameters> params, final ByteBuf msgBody) {
109 if (params == null || params.isEmpty()) {
110 msgBody.writeByte(0);
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);
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.orElseThrow().serializeExtendedParameter(param, buffer);
129 LOG.debug("Ignoring unregistered parameter {}", param);
133 final int length = buffer.writerIndex();
134 checkState(length <= Values.UNSIGNED_SHORT_MAX_VALUE);
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);
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()) {
152 optSer.orElseThrow().serializeParameter(param, buffer);
153 } catch (ParameterLengthOverflowException e) {
154 LOG.debug("Forcing extended parameter serialization", e);
158 LOG.debug("Ingnoring unregistered parameter {}", param);
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);
172 * Parses given byte array to BGP Open message.
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
180 public Open parseMessageBody(final ByteBuf body, final int messageLength,
181 final PeerSpecificParserConstraint constraint) throws BGPDocumentedException {
182 checkArgument(body != null, "Buffer cannot be null.");
184 if (body.readableBytes() < MIN_MSG_LENGTH) {
185 throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
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);
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);
197 final Ipv4AddressNoZone bgpId;
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);
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();
211 private List<BgpParameters> parseParameters(final ByteBuf buffer, final int length) throws BGPDocumentedException {
213 return ImmutableList.of();
215 if (LOG.isTraceEnabled()) {
216 LOG.trace("Started parsing of BGP parameter: {} length {}", ByteBufUtil.hexDump(buffer), length);
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);
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));
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);
245 if (buffer.readableBytes() <= lengthSize) {
246 throw new BGPDocumentedException("Malformed parameter encountered (" + buffer.readableBytes()
247 + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
249 final int paramLength = extendedLength.isPresent() ? buffer.readUnsignedShort() : buffer.readUnsignedByte();
250 final ByteBuf paramBody = buffer.readSlice(paramLength);
252 final BgpParameters param;
254 param = parser.orElseThrow().parseParameter(paramBody);
255 } catch (final BGPParsingException e) {
256 throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
259 params.add(verifyNotNull(param));
262 LOG.trace("Parsed BGP parameters: {}", params);
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();
274 if (length != Values.UNSIGNED_BYTE_MAX_VALUE) {
275 LOG.debug("Peer uses Extended Optional Parameters Length, but indicated RFC4271 length as {}", length);
278 throw new BGPDocumentedException("Malformed Extended Length parameter encountered ("
279 + (length - 1) + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
281 final int avail = buffer.readableBytes();
283 throw new BGPDocumentedException("Buffer underrun: require 2 bytes, only " + avail + " bytes left",
284 BGPError.UNSPECIFIC_OPEN_ERROR);
287 return OptionalInt.of(buffer.readUnsignedShort());