Merge "Randomize port to allow concurrent execution"
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / message / BGPOpenMessageParser.java
index fa2c278784d98e4b290e2df3d8bfc23ee163ce30..c4c1c5fced134c847aaf7768c48e5ddaa8f6f877 100644 (file)
@@ -15,7 +15,11 @@ import java.util.Map.Entry;
 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.impl.message.open.BGPParameterParser;
+import org.opendaylight.protocol.bgp.parser.impl.message.open.SimpleParameterRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.MessageParser;
+import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
 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.AsNumber;
@@ -23,9 +27,11 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.Open;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.OpenBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.open.BgpParameters;
+import org.opendaylight.yangtools.yang.binding.Notification;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.primitives.UnsignedBytes;
@@ -33,7 +39,16 @@ import com.google.common.primitives.UnsignedBytes;
 /**
  * Parser for BGP Open message.
  */
-public final class BGPOpenMessageParser {
+public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
+       public static final int TYPE = 1;
+       public static final MessageParser PARSER;
+       public static final MessageSerializer SERIALIZER;
+
+       static {
+               final BGPOpenMessageParser p = new BGPOpenMessageParser(SimpleParameterRegistry.INSTANCE);
+               PARSER = p;
+               SERIALIZER = p;
+       }
 
        private static final Logger logger = LoggerFactory.getLogger(BGPOpenMessageParser.class);
 
@@ -47,8 +62,10 @@ public final class BGPOpenMessageParser {
 
        private static final int BGP_VERSION = 4;
 
-       private BGPOpenMessageParser() {
+       private final ParameterRegistry reg;
 
+       private BGPOpenMessageParser(final ParameterRegistry reg) {
+               this.reg = Preconditions.checkNotNull(reg);
        }
 
        /**
@@ -57,25 +74,29 @@ public final class BGPOpenMessageParser {
         * @param msg BGP Open message to be serialized.
         * @return BGP Open message converted to byte array
         */
-       public static byte[] put(final Open msg) {
-               if (msg == null)
+       @Override
+       public byte[] serializeMessage(final Notification msg) {
+               if (msg == null) {
                        throw new IllegalArgumentException("BGPOpen message cannot be null");
+               }
                logger.trace("Started serializing open message: {}", msg);
+               final Open open = (Open) msg;
 
                final Map<byte[], Integer> optParams = Maps.newHashMap();
 
                int optParamsLength = 0;
 
-               if (msg.getBgpParameters() != null) {
-                       for (final BgpParameters param : msg.getBgpParameters()) {
-                               final byte[] p = BGPParameterParser.put(param);
-                               optParams.put(p, p.length);
-                               optParamsLength += p.length;
+               if (open.getBgpParameters() != null) {
+                       for (final BgpParameters param : open.getBgpParameters()) {
+                               final byte[] p = reg.serializeParameter(param);
+                               if (p != null) {
+                                       optParams.put(p, p.length);
+                                       optParamsLength += p.length;
+                               }
                        }
                }
 
-               final byte[] msgBody = (msg.getBgpParameters() == null || msg.getBgpParameters().isEmpty()) ? new byte[MIN_MSG_LENGTH]
-                               : new byte[MIN_MSG_LENGTH + optParamsLength];
+               final byte[] msgBody = new byte[MIN_MSG_LENGTH + optParamsLength];
 
                int offset = 0;
 
@@ -83,17 +104,18 @@ public final class BGPOpenMessageParser {
                offset += VERSION_SIZE;
 
                // When our AS number does not fit into two bytes, we report it as AS_TRANS
-               int openAS = msg.getMyAsNumber();
-               if (openAS > 65535)
+               int openAS = open.getMyAsNumber();
+               if (openAS > 65535) {
                        openAS = 2345;
+               }
 
                System.arraycopy(ByteArray.longToBytes(openAS), 6, msgBody, offset, AS_SIZE);
                offset += AS_SIZE;
 
-               System.arraycopy(ByteArray.intToBytes(msg.getHoldTimer()), 2, msgBody, offset, HOLD_TIME_SIZE);
+               System.arraycopy(ByteArray.intToBytes(open.getHoldTimer()), 2, msgBody, offset, HOLD_TIME_SIZE);
                offset += HOLD_TIME_SIZE;
 
-               System.arraycopy(Ipv4Util.bytesForAddress(msg.getBgpIdentifier()), 0, msgBody, offset, BGP_ID_SIZE);
+               System.arraycopy(Ipv4Util.bytesForAddress(open.getBgpIdentifier()), 0, msgBody, offset, BGP_ID_SIZE);
                offset += BGP_ID_SIZE;
 
                msgBody[offset] = ByteArray.intToBytes(optParamsLength)[Integer.SIZE / Byte.SIZE - 1];
@@ -105,59 +127,97 @@ public final class BGPOpenMessageParser {
                                index += entry.getValue();
                        }
                }
-               logger.trace("Open message serialized to: {}", Arrays.toString(msgBody));
-               return msgBody;
+
+               final byte[] ret = MessageUtil.formatMessage(TYPE, msgBody);
+               logger.trace("Open message serialized to: {}", Arrays.toString(ret));
+               return ret;
        }
 
        /**
         * Parses given byte array to BGP Open message
         * 
-        * @param bytes byte array representing BGP Open message, without header
+        * @param body byte array representing BGP Open message, without header
         * @return BGP Open Message
         * @throws BGPDocumentedException if the parsing was unsuccessful
         */
-       public static Open parse(final byte[] bytes) throws BGPDocumentedException {
-               if (bytes == null || bytes.length == 0)
-                       throw new IllegalArgumentException("Byte array cannot be null or empty.");
-               logger.trace("Started parsing of open message: {}", Arrays.toString(bytes));
+       @Override
+       public Open parseMessageBody(final byte[] body, final int messageLength) throws BGPDocumentedException {
+               if (body == null) {
+                       throw new IllegalArgumentException("Byte array cannot be null.");
+               }
+               logger.trace("Started parsing of open message: {}", Arrays.toString(body));
 
-               if (bytes.length < MIN_MSG_LENGTH)
-                       throw new BGPDocumentedException("Open message too small.", BGPError.BAD_MSG_LENGTH, ByteArray.intToBytes(bytes.length));
-               if (UnsignedBytes.toInt(bytes[0]) != BGP_VERSION)
-                       throw new BGPDocumentedException("BGP Protocol version " + UnsignedBytes.toInt(bytes[0]) + " not supported.", BGPError.VERSION_NOT_SUPPORTED, ByteArray.subByte(
+               if (body.length < MIN_MSG_LENGTH) {
+                       throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
+               }
+               if (UnsignedBytes.toInt(body[0]) != BGP_VERSION) {
+                       throw new BGPDocumentedException("BGP Protocol version " + UnsignedBytes.toInt(body[0]) + " not supported.", BGPError.VERSION_NOT_SUPPORTED, ByteArray.subByte(
                                        ByteArray.intToBytes(BGP_VERSION), 2, 2));
+               }
 
                int offset = VERSION_SIZE;
-               final AsNumber as = new AsNumber(ByteArray.bytesToLong(ByteArray.subByte(bytes, offset, AS_SIZE)));
+               final AsNumber as = new AsNumber(ByteArray.bytesToLong(ByteArray.subByte(body, offset, AS_SIZE)));
                offset += AS_SIZE;
 
                // TODO: BAD_PEER_AS Error: when is an AS unacceptable?
 
-               final short holdTime = ByteArray.bytesToShort(ByteArray.subByte(bytes, offset, HOLD_TIME_SIZE));
+               final short holdTime = ByteArray.bytesToShort(ByteArray.subByte(body, offset, HOLD_TIME_SIZE));
                offset += HOLD_TIME_SIZE;
-               if (holdTime == 1 || holdTime == 2)
+               if (holdTime == 1 || holdTime == 2) {
                        throw new BGPDocumentedException("Hold time value not acceptable.", BGPError.HOLD_TIME_NOT_ACC);
+               }
 
                Ipv4Address bgpId = null;
                try {
-                       bgpId = Ipv4Util.addressForBytes(ByteArray.subByte(bytes, offset, BGP_ID_SIZE));
+                       bgpId = Ipv4Util.addressForBytes(ByteArray.subByte(body, offset, BGP_ID_SIZE));
                } catch (final IllegalArgumentException e) {
                        throw new BGPDocumentedException("BGP Identifier is not a valid IPv4 Address", BGPError.BAD_BGP_ID);
                }
                offset += BGP_ID_SIZE;
 
-               final int optLength = UnsignedBytes.toInt(bytes[offset]);
+               final int optLength = UnsignedBytes.toInt(body[offset]);
 
                List<BgpParameters> optParams = Lists.newArrayList();
                if (optLength > 0) {
+                       fillParams(ByteArray.subByte(body, MIN_MSG_LENGTH, optLength), optParams);
+               }
+               logger.trace("Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as, holdTime, bgpId, optParams);
+               return new OpenBuilder().setMyAsNumber(as.getValue().intValue()).setHoldTimer((int) holdTime).setBgpIdentifier(bgpId).setBgpParameters(
+                               optParams).build();
+       }
+
+       private void fillParams(final byte[] bytes, final List<BgpParameters> params) throws BGPDocumentedException {
+               if (bytes == null || bytes.length == 0) {
+                       throw new IllegalArgumentException("Byte array cannot be null or empty.");
+               }
+
+               logger.trace("Started parsing of BGP parameter: {}", Arrays.toString(bytes));
+               int byteOffset = 0;
+               while (byteOffset < bytes.length) {
+                       if (byteOffset + 2 >= bytes.length) {
+                               // FIXME: throw a BGPDocumentedException here?
+                               throw new IllegalArgumentException("Malformed parameter encountered (" + (bytes.length - byteOffset) + " bytes left)");
+                       }
+
+                       final int paramType = UnsignedBytes.toInt(bytes[byteOffset++]);
+                       final int paramLength = UnsignedBytes.toInt(bytes[byteOffset++]);
+                       final byte[] paramBody = ByteArray.subByte(bytes, byteOffset, paramLength);
+                       byteOffset += paramLength;
+
+                       final BgpParameters param;
                        try {
-                               optParams = BGPParameterParser.parse(ByteArray.subByte(bytes, MIN_MSG_LENGTH, optLength));
+                               param = reg.parseParameter(paramType, paramBody);
                        } catch (final BGPParsingException e) {
                                throw new BGPDocumentedException("Optional parameter not parsed: ." + e.getMessage(), BGPError.UNSPECIFIC_OPEN_ERROR);
                        }
+
+                       if (param != null) {
+                               params.add(param);
+                       } else {
+                               logger.debug("Ignoring BGP Parameter type: {}", paramType);
+                       }
                }
-               logger.trace("Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as, holdTime, bgpId, optParams);
-               return new OpenBuilder().setMyAsNumber(as.getValue().intValue()).setHoldTimer((int) holdTime).setBgpIdentifier(bgpId).setBgpParameters(
-                               optParams).build();
+
+               logger.trace("Parsed BGP parameters: {}", Arrays.toString(params.toArray()));
        }
 }