BUG-54 : switched channel pipeline to be protocol specific.
[bgpcep.git] / bgp / parser-impl / src / main / java / org / opendaylight / protocol / bgp / parser / impl / message / BGPUpdateMessageParser.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
9 package org.opendaylight.protocol.bgp.parser.impl.message;
10
11 import java.util.Arrays;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.Set;
15
16 import org.opendaylight.protocol.bgp.concepts.BGPAddressFamily;
17 import org.opendaylight.protocol.bgp.concepts.BGPSubsequentAddressFamily;
18 import org.opendaylight.protocol.bgp.concepts.BGPTableType;
19 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
20 import org.opendaylight.protocol.bgp.parser.BGPError;
21 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
22 import org.opendaylight.protocol.bgp.parser.BGPUpdateEvent;
23 import org.opendaylight.protocol.bgp.parser.BGPUpdateSynchronized;
24 import org.opendaylight.protocol.bgp.parser.impl.BGPMessageFactoryImpl;
25 import org.opendaylight.protocol.bgp.parser.impl.BGPUpdateEventBuilder;
26 import org.opendaylight.protocol.bgp.parser.impl.IPv6MP;
27 import org.opendaylight.protocol.bgp.parser.impl.PathAttribute;
28 import org.opendaylight.protocol.bgp.parser.impl.PathAttribute.TypeCode;
29 import org.opendaylight.protocol.bgp.parser.impl.message.update.PathAttributeParser;
30 import org.opendaylight.protocol.concepts.IPv4;
31 import org.opendaylight.protocol.concepts.IPv4Address;
32 import org.opendaylight.protocol.concepts.Prefix;
33 import org.opendaylight.protocol.util.ByteArray;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import com.google.common.collect.Lists;
38
39 /**
40  * LENGTH fields, that denote the length of the fields with variable length, have fixed SIZE.
41  * 
42  * @see <a href="http://tools.ietf.org/html/rfc4271#section-4.3">BGP-4 Update Message Format</a>
43  * 
44  */
45 public class BGPUpdateMessageParser {
46
47         private static Logger logger = LoggerFactory.getLogger(BGPUpdateMessageParser.class);
48
49         /**
50          * Size of the withdrawn_routes_length field, in bytes.
51          */
52         public static final int WITHDRAWN_ROUTES_LENGTH_SIZE = 2;
53
54         /**
55          * Size of the total_path_attr_length field, in bytes.
56          */
57         public static final int TOTAL_PATH_ATTR_LENGTH_SIZE = 2;
58
59         // Constructors -------------------------------------------------------
60
61         public BGPUpdateMessageParser() {
62
63         }
64
65         // Getters & setters --------------------------------------------------
66
67         public static BGPUpdateEvent parse(final byte[] bytes, final int msgLength) throws BGPDocumentedException {
68                 if (bytes == null || bytes.length == 0)
69                         throw new IllegalArgumentException("Byte array cannot be null or empty.");
70                 logger.trace("Started parsing of update message: {}", Arrays.toString(bytes));
71
72                 int byteOffset = 0;
73
74                 final int withdrawnRoutesLength = ByteArray.bytesToInt(ByteArray.subByte(bytes, byteOffset, WITHDRAWN_ROUTES_LENGTH_SIZE));
75                 byteOffset += WITHDRAWN_ROUTES_LENGTH_SIZE;
76
77                 final BGPUpdateEventBuilder eventBuilder = new BGPUpdateEventBuilder();
78                 eventBuilder.setWithdrawnRoutesLength(withdrawnRoutesLength);
79
80                 Set<Prefix<IPv4Address>> withdrawnRoutes;
81                 if (withdrawnRoutesLength > 0) {
82                         withdrawnRoutes = IPv4.FAMILY.prefixListForBytes(ByteArray.subByte(bytes, byteOffset, withdrawnRoutesLength));
83                         byteOffset += withdrawnRoutesLength;
84                 } else {
85                         withdrawnRoutes = Collections.emptySet();
86                 }
87                 eventBuilder.setWithdrawnRoutes(withdrawnRoutes);
88
89                 final int totalPathAttrLength = ByteArray.bytesToInt(ByteArray.subByte(bytes, byteOffset, TOTAL_PATH_ATTR_LENGTH_SIZE));
90                 byteOffset += TOTAL_PATH_ATTR_LENGTH_SIZE;
91                 eventBuilder.setTotalPathAttrLength(totalPathAttrLength);
92
93                 if (withdrawnRoutesLength + totalPathAttrLength + BGPMessageFactoryImpl.COMMON_HEADER_LENGTH > msgLength)
94                         throw new BGPDocumentedException("Message length inconsistent with withdrawn router length.", BGPError.MALFORMED_ATTR_LIST);
95
96                 if (withdrawnRoutesLength == 0 && totalPathAttrLength == 0) {
97                         final BGPUpdateSynchronized event = new BGPUpdateSynchronized() {
98
99                                 private static final long serialVersionUID = 5709361453437508337L;
100
101                                 @Override
102                                 public BGPTableType getTableType() {
103                                         return new BGPTableType(BGPAddressFamily.IPv4, BGPSubsequentAddressFamily.Unicast);
104                                 }
105                         };
106                         return event;
107                 }
108
109                 List<PathAttribute> pathAttributes;
110                 if (totalPathAttrLength > 0) {
111                         pathAttributes = parsePathAttributes(ByteArray.subByte(bytes, byteOffset, totalPathAttrLength));
112                         byteOffset += totalPathAttrLength;
113                         if (pathAttributes.get(0).getType() == TypeCode.MP_UNREACH_NLRI && totalPathAttrLength == 6) {
114                                 if (pathAttributes.get(0).getValue() instanceof IPv6MP) {
115                                         final BGPUpdateEvent event = new BGPUpdateSynchronized() {
116
117                                                 private static final long serialVersionUID = -6026212683738125407L;
118
119                                                 @Override
120                                                 public BGPTableType getTableType() {
121                                                         return new BGPTableType(BGPAddressFamily.IPv6, BGPSubsequentAddressFamily.Unicast);
122                                                 }
123
124                                         };
125                                         return event;
126                                 } else if (pathAttributes.get(0).getValue() == null) {
127                                         final BGPUpdateSynchronized event = new BGPUpdateSynchronized() {
128
129                                                 private static final long serialVersionUID = 5888562784007786559L;
130
131                                                 @Override
132                                                 public BGPTableType getTableType() {
133                                                         return new BGPTableType(BGPAddressFamily.LinkState, BGPSubsequentAddressFamily.Unicast);
134                                                 }
135
136                                         };
137                                         return event;
138                                 }
139                         }
140                 } else {
141                         pathAttributes = Collections.emptyList();
142                 }
143                 eventBuilder.setPathAttributes(pathAttributes);
144
145                 final Set<Prefix<IPv4Address>> nlri = IPv4.FAMILY.prefixListForBytes(ByteArray.subByte(bytes, byteOffset, bytes.length - byteOffset));
146                 eventBuilder.setNlri(nlri);
147
148                 try {
149                         logger.trace("Update message was parsed.");
150                         return eventBuilder.buildEvent();
151                 } catch (final BGPParsingException e) {
152                         throw new BGPDocumentedException("Parsing unsuccessful: {}" + e.getMessage(), BGPError.MALFORMED_ATTR_LIST);
153                 }
154         }
155
156         /**
157          * Parse different Path Attributes from given bytes.
158          * 
159          * @param bytes byte array to be parsed
160          * @return list of Path Attributes
161          * @throws BGPParsingException
162          */
163         private static List<PathAttribute> parsePathAttributes(byte[] bytes) throws BGPDocumentedException {
164                 if (bytes.length == 0) {
165                         return Collections.emptyList();
166                 }
167                 final List<PathAttribute> list = Lists.newArrayList();
168                 while (bytes.length != 0) {
169                         PathAttribute attr;
170                         try {
171                                 attr = PathAttributeParser.parseAttribute(bytes);
172                                 bytes = ByteArray.cutBytes(bytes,
173                                                 PathAttribute.ATTR_FLAGS_SIZE + PathAttribute.ATTR_TYPE_CODE_SIZE + attr.getAttrLengthSize() + attr.getLength());
174                                 list.add(attr);
175                         } catch (final BGPParsingException e) {
176                                 logger.warn("Could not parse BGP attributes: {}", e.getMessage(), e);
177                                 throw new BGPDocumentedException("Could not parse BGP attributes.", BGPError.MALFORMED_ATTR_LIST);
178                         }
179                 }
180                 return list;
181         }
182 }