Bump MDSAL to 4.0.0
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / message / BGPOpenMessageParser.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.protocol.bgp.parser.impl.message;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import io.netty.buffer.ByteBuf;
14 import io.netty.buffer.ByteBufUtil;
15 import io.netty.buffer.Unpooled;
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
19 import org.opendaylight.protocol.bgp.parser.BGPError;
20 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
21 import org.opendaylight.protocol.bgp.parser.spi.MessageParser;
22 import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer;
23 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
24 import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
25 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
26 import org.opendaylight.protocol.util.Ipv4Util;
27 import org.opendaylight.protocol.util.Values;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Open;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
33 import org.opendaylight.yangtools.yang.binding.Notification;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * Parser for BGP Open message.
39  */
40 public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
41
42     private static final Logger LOG = LoggerFactory.getLogger(BGPOpenMessageParser.class);
43
44     public static final int TYPE = 1;
45
46     private static final int VERSION_SIZE = 1;
47     private static final int AS_SIZE = 2;
48     private static final int HOLD_TIME_SIZE = 2;
49     private static final int BGP_ID_SIZE = 4;
50     private static final int OPT_PARAM_LENGTH_SIZE = 1;
51
52     private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE
53             + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
54
55     private static final int BGP_VERSION = 4;
56
57     public static final int AS_TRANS = 23456;
58
59     private final ParameterRegistry reg;
60
61     public BGPOpenMessageParser(final ParameterRegistry reg) {
62         this.reg = requireNonNull(reg);
63     }
64
65     /**
66      * Serializes given BGP Open message to byte array, without the header.
67      *
68      * @param msg BGP Open message to be serialized.
69      * @param bytes ByteBuf where the message will be serialized
70      */
71     @Override
72     public void serializeMessage(final Notification msg, final ByteBuf bytes) {
73         checkArgument(msg instanceof Open, "Message needs to be of type Open, not %s", msg);
74         final Open open = (Open) msg;
75         final ByteBuf msgBody = Unpooled.buffer();
76
77         msgBody.writeByte(BGP_VERSION);
78
79         // When our AS number does not fit into two bytes, we report it as AS_TRANS
80         int openAS = open.getMyAsNumber();
81         if (openAS > Values.UNSIGNED_SHORT_MAX_VALUE) {
82             openAS = AS_TRANS;
83         }
84         msgBody.writeShort(openAS);
85         msgBody.writeShort(open.getHoldTimer());
86         msgBody.writeBytes(Ipv4Util.bytesForAddress(open.getBgpIdentifier()));
87
88         final ByteBuf paramsBuffer = Unpooled.buffer();
89         final List<BgpParameters> params = open.getBgpParameters();
90         if (params != null) {
91             for (final BgpParameters param : params) {
92                 this.reg.serializeParameter(param, paramsBuffer);
93             }
94         }
95         final int paramsLength = paramsBuffer.writerIndex();
96         if (paramsLength > 255) {
97             LOG.error("OPEN message message optional parameter length {} exceeds maximum length supported by BGP. "
98                 + "Adjust advertized capabilities toward the peer to fit into limit of 255 bytes by trimming {}",
99                 paramsLength, params);
100             throw new IllegalArgumentException(String.format(
101                 "Cannot encode OPEN message because optional parameter length %s exceeds length field size.",
102                 paramsLength));
103         }
104         msgBody.writeByte(paramsLength);
105         msgBody.writeBytes(paramsBuffer);
106
107         MessageUtil.formatMessage(TYPE, msgBody, bytes);
108     }
109
110     /**
111      * Parses given byte array to BGP Open message.
112      *
113      * @param body byte array representing BGP Open message, without header
114      * @param messageLength the length of the message
115      * @return {@link Open} BGP Open Message
116      * @throws BGPDocumentedException if the parsing was unsuccessful
117      */
118     @Override
119     public Open parseMessageBody(final ByteBuf body, final int messageLength,
120             final PeerSpecificParserConstraint constraint) throws BGPDocumentedException {
121         checkArgument(body != null, "Buffer cannot be null.");
122
123         if (body.readableBytes() < MIN_MSG_LENGTH) {
124             throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
125         }
126         final int version = body.readUnsignedByte();
127         if (version != BGP_VERSION) {
128             throw new BGPDocumentedException("BGP Protocol version " + version + " not supported.",
129                     BGPError.VERSION_NOT_SUPPORTED);
130         }
131         final AsNumber as = new AsNumber((long) body.readUnsignedShort());
132         final int holdTime = body.readUnsignedShort();
133         if (holdTime == 1 || holdTime == 2) {
134             throw new BGPDocumentedException("Hold time value not acceptable.", BGPError.HOLD_TIME_NOT_ACC);
135         }
136         Ipv4Address bgpId;
137         try {
138             bgpId = Ipv4Util.addressForByteBuf(body);
139         } catch (final IllegalArgumentException e) {
140             throw new BGPDocumentedException("BGP Identifier is not a valid IPv4 Address", BGPError.BAD_BGP_ID, e);
141         }
142         final int optLength = body.readUnsignedByte();
143
144         final List<BgpParameters> optParams = new ArrayList<>();
145         if (optLength > 0) {
146             fillParams(body.slice(), optParams);
147         }
148         LOG.debug("BGP Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as,
149                 holdTime, bgpId, optParams);
150         return new OpenBuilder().setMyAsNumber(as.getValue().intValue()).setHoldTimer(holdTime)
151                 .setBgpIdentifier(bgpId).setBgpParameters(optParams).build();
152     }
153
154     private void fillParams(final ByteBuf buffer, final List<BgpParameters> params) throws BGPDocumentedException {
155         checkArgument(buffer != null && buffer.isReadable(), "Buffer cannot be null or empty.");
156         if (LOG.isTraceEnabled()) {
157             LOG.trace("Started parsing of BGP parameter: {}", ByteBufUtil.hexDump(buffer));
158         }
159         while (buffer.isReadable()) {
160             if (buffer.readableBytes() <= 2) {
161                 throw new BGPDocumentedException("Malformed parameter encountered (" + buffer.readableBytes()
162                         + " bytes left)", BGPError.OPT_PARAM_NOT_SUPPORTED);
163             }
164             final int paramType = buffer.readUnsignedByte();
165             final int paramLength = buffer.readUnsignedByte();
166             final ByteBuf paramBody = buffer.readSlice(paramLength);
167
168             final BgpParameters param;
169             try {
170                 param = this.reg.parseParameter(paramType, paramBody);
171             } catch (final BGPParsingException e) {
172                 throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
173             }
174             if (param != null) {
175                 params.add(param);
176             } else {
177                 LOG.debug("Ignoring BGP Parameter type: {}", paramType);
178             }
179         }
180         LOG.trace("Parsed BGP parameters: {}", params);
181     }
182 }