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 a652b5e691aeae908d049eac384348ec2361b544..c4c1c5fced134c847aaf7768c48e5ddaa8f6f877 100644 (file)
@@ -14,16 +14,24 @@ 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.BGPParameter;
 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
-import org.opendaylight.protocol.bgp.parser.impl.message.open.BGPParameterParser;
-import org.opendaylight.protocol.bgp.parser.message.BGPOpenMessage;
-import org.opendaylight.protocol.concepts.IPv4Address;
+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;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+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;
@@ -31,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);
 
@@ -43,8 +60,12 @@ public final class BGPOpenMessageParser {
 
        private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
 
-       private BGPOpenMessageParser() {
+       private static final int BGP_VERSION = 4;
+
+       private final ParameterRegistry reg;
 
+       private BGPOpenMessageParser(final ParameterRegistry reg) {
+               this.reg = Preconditions.checkNotNull(reg);
        }
 
        /**
@@ -53,43 +74,48 @@ 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 BGPOpenMessage 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.getOptParams() != null) {
-                       for (final BGPParameter param : msg.getOptParams()) {
-                               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.getOptParams() == null || msg.getOptParams().isEmpty()) ? new byte[MIN_MSG_LENGTH]
-                               : new byte[MIN_MSG_LENGTH + optParamsLength];
+               final byte[] msgBody = new byte[MIN_MSG_LENGTH + optParamsLength];
 
                int offset = 0;
 
-               msgBody[offset] = ByteArray.intToBytes(BGPOpenMessage.BGP_VERSION)[(Integer.SIZE / Byte.SIZE) - 1];
+               msgBody[offset] = ByteArray.intToBytes(BGP_VERSION)[(Integer.SIZE / Byte.SIZE) - 1];
                offset += VERSION_SIZE;
 
                // When our AS number does not fit into two bytes, we report it as AS_TRANS
-               AsNumber openAS = msg.getMyAS();
-               if (openAS.getValue().longValue() > Integer.MAX_VALUE)
-                       openAS = new AsNumber((long) 2345);
+               int openAS = open.getMyAsNumber();
+               if (openAS > 65535) {
+                       openAS = 2345;
+               }
 
-               System.arraycopy(ByteArray.longToBytes(openAS.getValue()), 6, msgBody, offset, AS_SIZE);
+               System.arraycopy(ByteArray.longToBytes(openAS), 6, msgBody, offset, AS_SIZE);
                offset += AS_SIZE;
 
-               System.arraycopy(ByteArray.shortToBytes(msg.getHoldTime()), 0, msgBody, offset, HOLD_TIME_SIZE);
+               System.arraycopy(ByteArray.intToBytes(open.getHoldTimer()), 2, msgBody, offset, HOLD_TIME_SIZE);
                offset += HOLD_TIME_SIZE;
 
-               System.arraycopy(msg.getBgpId().getAddress(), 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];
@@ -101,58 +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 BGPOpenMessage 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]) != BGPOpenMessage.BGP_VERSION)
-                       throw new BGPDocumentedException("BGP Protocol version " + UnsignedBytes.toInt(bytes[0]) + " not supported.", BGPError.VERSION_NOT_SUPPORTED, ByteArray.subByte(
-                                       ByteArray.intToBytes(BGPOpenMessage.BGP_VERSION), 2, 2));
+               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;
+               Ipv4Address bgpId = null;
                try {
-                       bgpId = new IPv4Address(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<BGPParameter> optParams = Lists.newArrayList();
+               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 BGPOpenMessage(as, holdTime, bgpId, optParams);
+
+               logger.trace("Parsed BGP parameters: {}", Arrays.toString(params.toArray()));
        }
 }