BUG-113: make sure we capture TYPE
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / message / update / PathAttributeParser.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.update;
9
10 import java.util.List;
11
12 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
13 import org.opendaylight.protocol.bgp.parser.BGPError;
14 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
15 import org.opendaylight.protocol.bgp.parser.impl.message.update.AsPathSegmentParser.SegmentType;
16 import org.opendaylight.protocol.bgp.parser.spi.AttributeRegistry;
17 import org.opendaylight.protocol.concepts.IPv4;
18 import org.opendaylight.protocol.concepts.Ipv4Util;
19 import org.opendaylight.protocol.util.ByteArray;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.AsNumber;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev130918.PathAttributes1;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.linkstate.rev130918.PathAttributes1Builder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.Aggregator;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.AggregatorBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.AsPath;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.AsPathBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.Communities;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.ExtendedCommunities;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.LocalPref;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.LocalPrefBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.MultiExitDisc;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.MultiExitDiscBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.Origin;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.OriginBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.as.path.Segments;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.path.attributes.as.path.SegmentsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.PathAttributes;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.PathAttributesBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130918.PathAttributes2;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130918.PathAttributes2Builder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.BgpOrigin;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.ClusterIdentifier;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.CAListBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.CASetBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.as.path.segment.c.segment.c.a.list.AsSequence;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.CNextHop;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.CIpv4NextHopBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.c.next.hop.c.ipv4.next.hop.Ipv4NextHopBuilder;
50
51 import com.google.common.collect.Lists;
52 import com.google.common.primitives.UnsignedBytes;
53
54 /**
55  * 
56  * Parser for different Path Attributes. Each attributes has its own method for parsing.
57  * 
58  */
59 public class PathAttributeParser {
60
61         private static final int FLAGS_LENGTH = 1;
62
63         private static final int TYPE_LENGTH = 1;
64
65         private static AttributeRegistry reg = SimpleAttributeRegistry.INSTANCE;
66
67         private PathAttributeParser() {
68
69         }
70
71         /**
72          * Parse path attribute header (the same for all path attributes) and set type, length and value fields.
73          * 
74          * @param bytes byte array to be parsed.
75          * @return generic Path Attribute
76          * @throws BGPParsingException
77          * @throws BGPDocumentedException
78          */
79         public static PathAttributes parseAttribute(final byte[] bytes) throws BGPDocumentedException, BGPParsingException {
80                 if (bytes == null || bytes.length == 0) {
81                         throw new BGPParsingException("Insufficient length of byte array: " + bytes.length);
82                 }
83                 int byteOffset = 0;
84                 final PathAttributesBuilder builder = new PathAttributesBuilder();
85                 while (byteOffset < bytes.length) {
86                         final boolean[] bits = ByteArray.parseBits(bytes[0]);
87                         final boolean optional = bits[0];
88                         final int attrLength = (bits[3]) ? ByteArray.bytesToInt(ByteArray.subByte(bytes, 2, 2)) : UnsignedBytes.toInt(bytes[2]);
89                         final int hdrLength = FLAGS_LENGTH + TYPE_LENGTH + ((bits[3]) ? 2 : 1);
90
91                         final byte[] attrBody = ByteArray.subByte(bytes, hdrLength, attrLength);
92
93                         boolean found = reg.parseAttribute(UnsignedBytes.toInt(bytes[1]), attrBody, builder);
94                         if (!optional && !found) {
95                                 throw new BGPDocumentedException("Well known attribute not recognized.", BGPError.WELL_KNOWN_ATTR_NOT_RECOGNIZED);
96                         }
97
98                         byteOffset += hdrLength + attrLength;
99                 }
100                 return builder.build();
101         }
102
103         /**
104          * Parses ORIGIN from bytes.
105          * 
106          * @param bytes byte array to be parsed
107          * @return {@link Origin} BGP origin value
108          * @throws BGPDocumentedException
109          */
110         static Origin parseOrigin(final byte[] bytes) throws BGPDocumentedException {
111                 final BgpOrigin borigin = BgpOrigin.forValue(UnsignedBytes.toInt(bytes[0]));
112                 if (borigin == null) {
113                         throw new BGPDocumentedException("Unknown Origin type.", BGPError.ORIGIN_ATTR_NOT_VALID, new byte[] { (byte) 0x01, (byte) 0x01,
114                                         bytes[0] });
115                 }
116                 return new OriginBuilder().setValue(borigin).build();
117         }
118
119         /**
120          * Parses AS_PATH from bytes.
121          * 
122          * @param bytes byte array to be parsed
123          * @return new ASPath object
124          * @throws BGPDocumentedException if there is no AS_SEQUENCE present (mandatory)
125          * @throws BGPParsingException
126          */
127         static AsPath parseAsPath(final byte[] bytes) throws BGPDocumentedException, BGPParsingException {
128                 int byteOffset = 0;
129                 final List<Segments> ases = Lists.newArrayList();
130                 boolean isSequence = false;
131                 while (byteOffset < bytes.length) {
132                         final int type = UnsignedBytes.toInt(bytes[byteOffset]);
133                         final SegmentType segmentType = AsPathSegmentParser.parseType(type);
134                         if (segmentType == null) {
135                                 throw new BGPParsingException("AS Path segment type unknown : " + type);
136                         }
137                         byteOffset += AsPathSegmentParser.TYPE_LENGTH;
138
139                         final int count = UnsignedBytes.toInt(bytes[byteOffset]);
140                         byteOffset += AsPathSegmentParser.LENGTH_SIZE;
141
142                         if (segmentType == SegmentType.AS_SEQUENCE) {
143                                 final List<AsSequence> numbers = AsPathSegmentParser.parseAsSequence(count,
144                                                 ByteArray.subByte(bytes, byteOffset, count * AsPathSegmentParser.AS_NUMBER_LENGTH));
145                                 ases.add(new SegmentsBuilder().setCSegment(new CAListBuilder().setAsSequence(numbers).build()).build());
146                                 isSequence = true;
147                         } else {
148                                 final List<AsNumber> list = AsPathSegmentParser.parseAsSet(count,
149                                                 ByteArray.subByte(bytes, byteOffset, count * AsPathSegmentParser.AS_NUMBER_LENGTH));
150                                 ases.add(new SegmentsBuilder().setCSegment(new CASetBuilder().setAsSet(list).build()).build());
151
152                         }
153                         byteOffset += count * AsPathSegmentParser.AS_NUMBER_LENGTH;
154                 }
155
156                 if (!isSequence && bytes.length != 0) {
157                         throw new BGPDocumentedException("AS_SEQUENCE must be present in AS_PATH attribute.", BGPError.AS_PATH_MALFORMED);
158                 }
159                 return new AsPathBuilder().setSegments(ases).build();
160         }
161
162         /**
163          * Parse NEXT_HOP from bytes
164          * 
165          * @param bytes byte array to be parsed
166          * @return new NextHop object, it's always IPv4 (basic BGP-4)
167          */
168         static CNextHop parseNextHop(final byte[] bytes) {
169                 return new CIpv4NextHopBuilder().setIpv4NextHop(new Ipv4NextHopBuilder().setGlobal(Ipv4Util.addressForBytes(bytes)).build()).build();
170         }
171
172         /**
173          * Parse MULTI_EXIT_DISC (integer) from bytes
174          * 
175          * @param bytes byte array to be parsed
176          * @return integer representing MULTI_EXIT_DISC path attribute
177          */
178         static MultiExitDisc parseMultiExitDisc(final byte[] bytes) {
179                 return new MultiExitDiscBuilder().setMed(ByteArray.bytesToLong(bytes)).build();
180         }
181
182         /**
183          * Parse LOCAL_PREF (integer) from bytes
184          * 
185          * @param bytes byte array to be parsed
186          * @return integer representing LOCAL_PREF path attribute
187          */
188         static LocalPref parseLocalPref(final byte[] bytes) {
189                 return new LocalPrefBuilder().setPref(ByteArray.bytesToLong(bytes)).build();
190         }
191
192         /**
193          * Parse AGGREGATOR from bytes
194          * 
195          * @param bytes byte array to be parsed
196          * @return {@link Aggregator} BGP Aggregator
197          */
198         static Aggregator parseAggregator(final byte[] bytes) {
199                 final AsNumber asNumber = new AsNumber(ByteArray.bytesToLong(ByteArray.subByte(bytes, 0, AsPathSegmentParser.AS_NUMBER_LENGTH)));
200                 final Ipv4Address address = new Ipv4Address(IPv4.FAMILY.addressForBytes(
201                                 ByteArray.subByte(bytes, AsPathSegmentParser.AS_NUMBER_LENGTH, 4)).toString());
202                 return new AggregatorBuilder().setAsNumber(asNumber).setNetworkAddress(address).build();
203         }
204
205         /**
206          * Parse MP_REACH_NLRI from bytes
207          * 
208          * @param bytes byte array to be parsed
209          * @return new specific MPReach object with reachable flag set to true
210          * @throws BGPDocumentedException
211          */
212         static void parseMPReach(final PathAttributesBuilder b, final byte[] bytes) throws BGPDocumentedException {
213
214                 try {
215                         final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130918.PathAttributes1 a = new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130918.PathAttributes1Builder().setMpReachNlri(
216                                         MPReachParser.parseMPReach(bytes)).build();
217
218                         b.addAugmentation(
219                                         org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130918.PathAttributes1.class, a);
220                 } catch (final BGPParsingException e) {
221                         throw new BGPDocumentedException("Could not parse MP_REACH_NLRI: " + e.getMessage(), BGPError.OPT_ATTR_ERROR);
222                 }
223         }
224
225         /**
226          * Parse MP_UNREACH_NLRI from bytes
227          * 
228          * @param bytes byte array to be parsed
229          * @return new specific MPReach object with reachable flag set to false
230          * @throws BGPDocumentedException
231          */
232         static void parseMPUnreach(final PathAttributesBuilder b, final byte[] bytes) throws BGPDocumentedException {
233                 try {
234                         final PathAttributes2 a = new PathAttributes2Builder().setMpUnreachNlri(MPReachParser.parseMPUnreach(bytes)).build();
235
236                         b.addAugmentation(PathAttributes2.class, a);
237                 } catch (final BGPParsingException e) {
238                         throw new BGPDocumentedException("Could not parse MP_UNREACH_NLRI: " + e.getMessage(), BGPError.OPT_ATTR_ERROR);
239                 }
240         }
241
242         /**
243          * Parse set of EXTENDED_COMMUNITIES from bytes
244          * 
245          * @param bytes byte array to be parsed
246          * @return new specific Extended Community object
247          * @throws BGPDocumentedException l
248          */
249         static List<ExtendedCommunities> parseExtendedCommunities(final byte[] bytes) throws BGPDocumentedException {
250                 final List<ExtendedCommunities> set = Lists.newArrayList();
251                 int i = 0;
252                 while (i < bytes.length) {
253                         set.add((ExtendedCommunities) CommunitiesParser.parseExtendedCommunity(ByteArray.subByte(bytes, i,
254                                         CommunitiesParser.EXTENDED_COMMUNITY_LENGTH)));
255                         i += CommunitiesParser.EXTENDED_COMMUNITY_LENGTH;
256                 }
257                 return set;
258         }
259
260         /**
261          * Parse set of COMMUNITIES from bytes
262          * 
263          * @param bytes byte array to be parsed
264          * @return new specific Community object
265          * @throws BGPDocumentedException
266          */
267         static List<Communities> parseCommunities(final byte[] bytes) throws BGPDocumentedException {
268                 final List<Communities> set = Lists.newArrayList();
269                 int i = 0;
270                 while (i < bytes.length) {
271                         set.add((Communities) CommunitiesParser.parseCommunity(ByteArray.subByte(bytes, i, CommunitiesParser.COMMUNITY_LENGTH)));
272                         i += CommunitiesParser.COMMUNITY_LENGTH;
273                 }
274                 return set;
275         }
276
277         /**
278          * Parse list of Cluster Identifiers.
279          * 
280          * @param bytes byte array to be parsed
281          * @return new List of Cluster Identifiers
282          */
283         static List<ClusterIdentifier> parseClusterList(final byte[] bytes) {
284                 final List<ClusterIdentifier> list = Lists.newArrayList();
285                 int i = 0;
286                 while (i < bytes.length) {
287                         list.add(new ClusterIdentifier(ByteArray.subByte(bytes, i, 4)));
288                         i += 4;
289                 }
290                 return list;
291         }
292
293         /**
294          * Parses ORIGINATOR_ID, which is BGP Identifier, which is IP address of the speaker.
295          * 
296          * @param bytes byte array to be parsed
297          * @return IP address of the speaker
298          */
299         static byte[] parseOriginatorId(final byte[] bytes) {
300                 if (bytes.length != 4) {
301                         throw new IllegalArgumentException("Length of byte array for ORIGINATOR_ID should be 4, but is " + bytes.length);
302                 }
303                 return bytes;
304         }
305
306         /**
307          * Parse LINK_STATE from bytes
308          * 
309          * @param bytes byte array to be parsed
310          * @return Map, where the key is the type of a tlv and the value is the value of the tlv
311          * @throws BGPParsingException
312          */
313         static void parseLinkState(final PathAttributesBuilder builder, final byte[] bytes) throws BGPParsingException {
314                 final PathAttributes1 a = new PathAttributes1Builder().setLinkstatePathAttribute(LinkStateParser.parseLinkState(bytes)).build();
315                 builder.addAugmentation(PathAttributes1.class, a);
316         }
317 }