X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=bgp%2Fparser-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fprotocol%2Fbgp%2Fparser%2Fimpl%2Fmessage%2FBGPUpdateMessageParser.java;h=b1f9d30d831b048e64b5272748014e505d1100b6;hb=108c60f69f75c76bd11aa1c07875ee7b1924ef03;hp=bc2f5f324f6ce370fa5014e57546263cb650052f;hpb=fef52c72498ea2e4d84ea6686f708fd9898ff0ac;p=bgpcep.git diff --git a/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java b/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java old mode 100644 new mode 100755 index bc2f5f324f..b1f9d30d83 --- a/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java +++ b/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java @@ -5,107 +5,206 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.protocol.bgp.parser.impl.message; -import java.util.Arrays; -import java.util.List; +import static java.util.Objects.requireNonNull; +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.ArrayList; +import java.util.List; import org.opendaylight.protocol.bgp.parser.BGPDocumentedException; import org.opendaylight.protocol.bgp.parser.BGPError; import org.opendaylight.protocol.bgp.parser.BGPParsingException; +import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl; +import org.opendaylight.protocol.bgp.parser.impl.message.update.AsPathAttributeParser; +import org.opendaylight.protocol.bgp.parser.impl.message.update.NextHopAttributeParser; +import org.opendaylight.protocol.bgp.parser.impl.message.update.OriginAttributeParser; import org.opendaylight.protocol.bgp.parser.spi.AttributeRegistry; import org.opendaylight.protocol.bgp.parser.spi.MessageParser; -import org.opendaylight.protocol.concepts.Ipv4Util; -import org.opendaylight.protocol.util.ByteArray; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.Update; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.UpdateBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.NlriBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.PathAttributes; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.WithdrawnRoutesBuilder; +import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer; +import org.opendaylight.protocol.bgp.parser.spi.MessageUtil; +import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupportUtil; +import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil; +import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint; +import org.opendaylight.protocol.util.ByteBufWriteUtil; +import org.opendaylight.protocol.util.Ipv4Util; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.UpdateBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.NlriBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.WithdrawnRoutes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.WithdrawnRoutesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily; +import org.opendaylight.yangtools.yang.binding.Notification; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Preconditions; - /** * LENGTH fields, that denote the length of the fields with variable length, have fixed SIZE. - * + * * @see BGP-4 Update Message Format - * */ -public class BGPUpdateMessageParser implements MessageParser { - public static final int TYPE = 2; - - private static Logger logger = LoggerFactory.getLogger(BGPUpdateMessageParser.class); - - /** - * Size of the withdrawn_routes_length field, in bytes. - */ - public static final int WITHDRAWN_ROUTES_LENGTH_SIZE = 2; - - /** - * Size of the total_path_attr_length field, in bytes. - */ - public static final int TOTAL_PATH_ATTR_LENGTH_SIZE = 2; - - private final AttributeRegistry reg; - - // Constructors ------------------------------------------------------- - public BGPUpdateMessageParser(final AttributeRegistry reg) { - this.reg = Preconditions.checkNotNull(reg); - } - - // Getters & setters -------------------------------------------------- - - @Override - public Update parseMessageBody(final byte[] body, final int messageLength) throws BGPDocumentedException { - if (body == null || body.length == 0) { - throw new IllegalArgumentException("Byte array cannot be null or empty."); - } - logger.trace("Started parsing of update message: {}", Arrays.toString(body)); - - int byteOffset = 0; - - final int withdrawnRoutesLength = ByteArray.bytesToInt(ByteArray.subByte(body, byteOffset, WITHDRAWN_ROUTES_LENGTH_SIZE)); - byteOffset += WITHDRAWN_ROUTES_LENGTH_SIZE; - - final UpdateBuilder eventBuilder = new UpdateBuilder(); - - if (withdrawnRoutesLength > 0) { - final List withdrawnRoutes = Ipv4Util.prefixListForBytes(ByteArray.subByte(body, byteOffset, withdrawnRoutesLength)); - byteOffset += withdrawnRoutesLength; - eventBuilder.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setWithdrawnRoutes(withdrawnRoutes).build()); - } - - final int totalPathAttrLength = ByteArray.bytesToInt(ByteArray.subByte(body, byteOffset, TOTAL_PATH_ATTR_LENGTH_SIZE)); - byteOffset += TOTAL_PATH_ATTR_LENGTH_SIZE; - - if (withdrawnRoutesLength + totalPathAttrLength > body.length) { - throw new BGPDocumentedException("Message length inconsistent with withdrawn router length.", BGPError.MALFORMED_ATTR_LIST); - } - - if (withdrawnRoutesLength == 0 && totalPathAttrLength == 0) { - return eventBuilder.build(); - } - - try { - if (totalPathAttrLength > 0) { - final PathAttributes pathAttributes = reg.parseAttributes(ByteArray.subByte(body, byteOffset, - totalPathAttrLength)); - byteOffset += totalPathAttrLength; - eventBuilder.setPathAttributes(pathAttributes); - } - } catch (final BGPParsingException e) { - logger.warn("Could not parse BGP attributes: {}", e.getMessage(), e); - throw new BGPDocumentedException("Could not parse BGP attributes.", BGPError.MALFORMED_ATTR_LIST); - } - - final List nlri = Ipv4Util.prefixListForBytes(ByteArray.subByte(body, byteOffset, body.length - byteOffset)); - eventBuilder.setNlri(new NlriBuilder().setNlri(nlri).build()); - - logger.trace("Update message was parsed."); - return eventBuilder.build(); - } +public final class BGPUpdateMessageParser implements MessageParser, MessageSerializer { + + private static final Logger LOG = LoggerFactory.getLogger(BGPUpdateMessageParser.class); + + public static final int TYPE = 2; + + private static final int WITHDRAWN_ROUTES_LENGTH_SIZE = 2; + + private static final int TOTAL_PATH_ATTR_LENGTH_SIZE = 2; + + private final AttributeRegistry reg; + + public BGPUpdateMessageParser(final AttributeRegistry reg) { + this.reg = requireNonNull(reg); + } + + @Override + public Update parseMessageBody(final ByteBuf buffer, final int messageLength) throws BGPDocumentedException { + return parseMessageBody(buffer, messageLength, null); + } + + @Override + public void serializeMessage(final Notification message, final ByteBuf bytes) { + Preconditions.checkArgument(message instanceof Update, "Message needs to be of type Update"); + final Update update = (Update) message; + + final ByteBuf messageBody = Unpooled.buffer(); + final List withdrawnRoutes = update.getWithdrawnRoutes(); + if (withdrawnRoutes != null) { + final ByteBuf withdrawnRoutesBuf = Unpooled.buffer(); + withdrawnRoutes.forEach(withdrawnRoute -> writePathIdPrefix(withdrawnRoutesBuf, withdrawnRoute.getPathId(), + withdrawnRoute.getPrefix())); + messageBody.writeShort(withdrawnRoutesBuf.writerIndex()); + messageBody.writeBytes(withdrawnRoutesBuf); + } else { + messageBody.writeZero(WITHDRAWN_ROUTES_LENGTH_SIZE); + } + if (update.getAttributes() != null) { + final ByteBuf pathAttributesBuf = Unpooled.buffer(); + this.reg.serializeAttribute(update.getAttributes(), pathAttributesBuf); + messageBody.writeShort(pathAttributesBuf.writerIndex()); + messageBody.writeBytes(pathAttributesBuf); + } else { + messageBody.writeZero(TOTAL_PATH_ATTR_LENGTH_SIZE); + } + final List nlris = update.getNlri(); + if (nlris != null) { + nlris.forEach(nlri -> writePathIdPrefix(messageBody, nlri.getPathId(), nlri.getPrefix())); + } + MessageUtil.formatMessage(TYPE, messageBody, bytes); + } + + private void writePathIdPrefix(final ByteBuf byteBuf, final PathId pathId, final Ipv4Prefix ipv4Prefix) { + PathIdUtil.writePathId(pathId, byteBuf); + ByteBufWriteUtil.writeMinimalPrefix(ipv4Prefix, byteBuf); + } + + /** + * Parse Update message from buffer. + * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes. + * + * @param buffer Encoded BGP message in ByteBuf + * @param messageLength Length of the BGP message + * @param constraint Peer specific constraints + * @return Parsed Update message body + */ + @Override + public Update parseMessageBody(final ByteBuf buffer, final int messageLength, + final PeerSpecificParserConstraint constraint) throws BGPDocumentedException { + Preconditions.checkArgument(buffer != null && buffer.isReadable(), + "Buffer cannot be null or empty."); + + final UpdateBuilder builder = new UpdateBuilder(); + final boolean isMultiPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint, + new BgpTableTypeImpl(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class)); + + final int withdrawnRoutesLength = buffer.readUnsignedShort(); + if (withdrawnRoutesLength > 0) { + final List withdrawnRoutes = new ArrayList<>(); + final ByteBuf withdrawnRoutesBuffer = buffer.readBytes(withdrawnRoutesLength); + while (withdrawnRoutesBuffer.isReadable()) { + final WithdrawnRoutesBuilder withdrawnRoutesBuilder = new WithdrawnRoutesBuilder(); + if (isMultiPathSupported) { + withdrawnRoutesBuilder.setPathId(PathIdUtil.readPathId(withdrawnRoutesBuffer)); + } + withdrawnRoutesBuilder.setPrefix(Ipv4Util.prefixForByteBuf(withdrawnRoutesBuffer)); + withdrawnRoutes.add(withdrawnRoutesBuilder.build()); + } + builder.setWithdrawnRoutes(withdrawnRoutes); + } + final int totalPathAttrLength = buffer.readUnsignedShort(); + + if (withdrawnRoutesLength == 0 && totalPathAttrLength == 0) { + return builder.build(); + } + if (totalPathAttrLength > 0) { + try { + final Attributes attributes + = this.reg.parseAttributes(buffer.readSlice(totalPathAttrLength), constraint); + builder.setAttributes(attributes); + } catch (final RuntimeException | BGPParsingException e) { + // Catch everything else and turn it into a BGPDocumentedException + throw new BGPDocumentedException("Could not parse BGP attributes.", BGPError.MALFORMED_ATTR_LIST, e); + } + } + final List nlri = new ArrayList<>(); + while (buffer.isReadable()) { + final NlriBuilder nlriBuilder = new NlriBuilder(); + if (isMultiPathSupported) { + nlriBuilder.setPathId(PathIdUtil.readPathId(buffer)); + } + nlriBuilder.setPrefix(Ipv4Util.prefixForByteBuf(buffer)); + nlri.add(nlriBuilder.build()); + } + if (!nlri.isEmpty()) { + builder.setNlri(nlri); + } + final Update msg = builder.build(); + checkMandatoryAttributesPresence(msg); + LOG.debug("BGP Update message was parsed {}.", msg); + return msg; + } + + /** + * Check for presence of well known mandatory path attributes + * ORIGIN, AS_PATH and NEXT_HOP in Update message + * + * @param message Update message + * @throws BGPDocumentedException + */ + private static void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException { + requireNonNull(message, "Update message cannot be null"); + + final Attributes attrs = message.getAttributes(); + + if (message.getNlri() != null) { + if (attrs == null || attrs.getCNextHop() == null) { + throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "NEXT_HOP", + BGPError.WELL_KNOWN_ATTR_MISSING, + new byte[] { NextHopAttributeParser.TYPE }); + } + } + + if (MessageUtil.isAnyNlriPresent(message)) { + if (attrs == null || attrs.getOrigin() == null) { + throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "ORIGIN", + BGPError.WELL_KNOWN_ATTR_MISSING, + new byte[] { OriginAttributeParser.TYPE }); + } + + if (attrs.getAsPath() == null) { + throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "AS_PATH", + BGPError.WELL_KNOWN_ATTR_MISSING, + new byte[] { AsPathAttributeParser.TYPE }); + } + } + } }