Merge changes I6885e623,Ie5e68f28
[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 java.util.Arrays;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14
15 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
16 import org.opendaylight.protocol.bgp.parser.BGPError;
17 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
18 import org.opendaylight.protocol.bgp.parser.spi.MessageParser;
19 import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer;
20 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
21 import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
22 import org.opendaylight.protocol.concepts.Ipv4Util;
23 import org.opendaylight.protocol.util.ByteArray;
24 import org.opendaylight.protocol.util.Values;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Open;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.OpenBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.open.BgpParameters;
30 import org.opendaylight.yangtools.yang.binding.Notification;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.google.common.base.Preconditions;
35 import com.google.common.collect.Lists;
36 import com.google.common.collect.Maps;
37 import com.google.common.primitives.UnsignedBytes;
38
39 /**
40  * Parser for BGP Open message.
41  */
42 public final class BGPOpenMessageParser implements MessageParser, MessageSerializer {
43         public static final int TYPE = 1;
44
45         private static final Logger logger = LoggerFactory.getLogger(BGPOpenMessageParser.class);
46
47         private static final int VERSION_SIZE = 1;
48         private static final int AS_SIZE = 2;
49         private static final int HOLD_TIME_SIZE = 2;
50         private static final int BGP_ID_SIZE = 4;
51         private static final int OPT_PARAM_LENGTH_SIZE = 1;
52
53         private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE + HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
54
55         private static final int BGP_VERSION = 4;
56
57         private static final int AS_TRANS = 2345;
58
59         private final ParameterRegistry reg;
60
61         public BGPOpenMessageParser(final ParameterRegistry reg) {
62                 this.reg = Preconditions.checkNotNull(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          * @return BGP Open message converted to byte array
70          */
71         @Override
72         public byte[] serializeMessage(final Notification msg) {
73                 if (msg == null) {
74                         throw new IllegalArgumentException("BGPOpen message cannot be null");
75                 }
76                 logger.trace("Started serializing open message: {}", msg);
77                 final Open open = (Open) msg;
78
79                 final Map<byte[], Integer> optParams = Maps.newHashMap();
80
81                 int optParamsLength = 0;
82
83                 if (open.getBgpParameters() != null) {
84                         for (final BgpParameters param : open.getBgpParameters()) {
85                                 final byte[] p = this.reg.serializeParameter(param);
86                                 if (p != null) {
87                                         optParams.put(p, p.length);
88                                         optParamsLength += p.length;
89                                 }
90                         }
91                 }
92                 final byte[] msgBody = new byte[MIN_MSG_LENGTH + optParamsLength];
93
94                 int offset = 0;
95
96                 msgBody[offset] = UnsignedBytes.checkedCast(BGP_VERSION);
97                 offset += VERSION_SIZE;
98
99                 // When our AS number does not fit into two bytes, we report it as AS_TRANS
100                 int openAS = open.getMyAsNumber();
101                 if (openAS > Values.UNSIGNED_SHORT_MAX_VALUE) {
102                         openAS = AS_TRANS;
103                 }
104                 System.arraycopy(ByteArray.longToBytes(openAS, AS_SIZE), 0, msgBody, offset, AS_SIZE);
105                 offset += AS_SIZE;
106
107                 System.arraycopy(ByteArray.intToBytes(open.getHoldTimer(), HOLD_TIME_SIZE), 0, msgBody, offset, HOLD_TIME_SIZE);
108                 offset += HOLD_TIME_SIZE;
109
110                 System.arraycopy(Ipv4Util.bytesForAddress(open.getBgpIdentifier()), 0, msgBody, offset, BGP_ID_SIZE);
111                 offset += BGP_ID_SIZE;
112
113                 msgBody[offset] = UnsignedBytes.checkedCast(optParamsLength);
114
115                 int index = MIN_MSG_LENGTH;
116                 if (optParams != null) {
117                         for (final Entry<byte[], Integer> entry : optParams.entrySet()) {
118                                 System.arraycopy(entry.getKey(), 0, msgBody, index, entry.getValue());
119                                 index += entry.getValue();
120                         }
121                 }
122                 final byte[] ret = MessageUtil.formatMessage(TYPE, msgBody);
123                 logger.trace("Open message serialized to: {}", Arrays.toString(ret));
124                 return ret;
125         }
126
127         /**
128          * Parses given byte array to BGP Open message
129          * 
130          * @param body byte array representing BGP Open message, without header
131          * @return BGP Open Message
132          * @throws BGPDocumentedException if the parsing was unsuccessful
133          */
134         @Override
135         public Open parseMessageBody(final byte[] body, final int messageLength) throws BGPDocumentedException {
136                 if (body == null) {
137                         throw new IllegalArgumentException("Byte array cannot be null.");
138                 }
139                 logger.trace("Started parsing of open message: {}", Arrays.toString(body));
140
141                 if (body.length < MIN_MSG_LENGTH) {
142                         throw BGPDocumentedException.badMessageLength("Open message too small.", messageLength);
143                 }
144                 if (UnsignedBytes.toInt(body[0]) != BGP_VERSION) {
145                         throw new BGPDocumentedException("BGP Protocol version " + UnsignedBytes.toInt(body[0]) + " not supported.", BGPError.VERSION_NOT_SUPPORTED);
146                 }
147                 int offset = VERSION_SIZE;
148                 final AsNumber as = new AsNumber(ByteArray.bytesToLong(ByteArray.subByte(body, offset, AS_SIZE)));
149                 offset += AS_SIZE;
150
151                 // TODO: BAD_PEER_AS Error: when is an AS unacceptable?
152
153                 final short holdTime = ByteArray.bytesToShort(ByteArray.subByte(body, offset, HOLD_TIME_SIZE));
154                 offset += HOLD_TIME_SIZE;
155                 if (holdTime == 1 || holdTime == 2) {
156                         throw new BGPDocumentedException("Hold time value not acceptable.", BGPError.HOLD_TIME_NOT_ACC);
157                 }
158                 Ipv4Address bgpId = null;
159                 try {
160                         bgpId = Ipv4Util.addressForBytes(ByteArray.subByte(body, offset, BGP_ID_SIZE));
161                 } catch (final IllegalArgumentException e) {
162                         throw new BGPDocumentedException("BGP Identifier is not a valid IPv4 Address", BGPError.BAD_BGP_ID, e);
163                 }
164                 offset += BGP_ID_SIZE;
165
166                 final int optLength = UnsignedBytes.toInt(body[offset]);
167
168                 final List<BgpParameters> optParams = Lists.newArrayList();
169                 if (optLength > 0) {
170                         fillParams(ByteArray.subByte(body, MIN_MSG_LENGTH, optLength), optParams);
171                 }
172                 logger.trace("Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as, holdTime, bgpId, optParams);
173                 return new OpenBuilder().setMyAsNumber(as.getValue().intValue()).setHoldTimer((int) holdTime).setBgpIdentifier(bgpId).setBgpParameters(
174                                 optParams).build();
175         }
176
177         private void fillParams(final byte[] bytes, final List<BgpParameters> params) throws BGPDocumentedException {
178                 if (bytes == null || bytes.length == 0) {
179                         throw new IllegalArgumentException("Byte array cannot be null or empty.");
180                 }
181                 logger.trace("Started parsing of BGP parameter: {}", Arrays.toString(bytes));
182                 int byteOffset = 0;
183                 while (byteOffset < bytes.length) {
184                         if (byteOffset + 2 >= bytes.length) {
185                                 throw new BGPDocumentedException("Malformed parameter encountered (" + (bytes.length - byteOffset) + " bytes left)", BGPError.OPT_PARAM_NOT_SUPPORTED);
186                         }
187                         final int paramType = UnsignedBytes.toInt(bytes[byteOffset++]);
188                         final int paramLength = UnsignedBytes.toInt(bytes[byteOffset++]);
189                         final byte[] paramBody = ByteArray.subByte(bytes, byteOffset, paramLength);
190                         byteOffset += paramLength;
191
192                         final BgpParameters param;
193                         try {
194                                 param = this.reg.parseParameter(paramType, paramBody);
195                         } catch (final BGPParsingException e) {
196                                 throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
197                         }
198                         if (param != null) {
199                                 params.add(param);
200                         } else {
201                                 logger.debug("Ignoring BGP Parameter type: {}", paramType);
202                         }
203                 }
204                 logger.trace("Parsed BGP parameters: {}", Arrays.toString(params.toArray()));
205         }
206 }