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