blob: afde4d82421d84f14df3fecb644296304e59625c [file] [log] [blame]
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001/*
Jonathan Hartf4bd0482017-01-27 15:11:18 -08002 * Copyright 2017-present Open Networking Laboratory
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jonathan Hartf4bd0482017-01-27 15:11:18 -080016
Jonathan Hart41349e92015-02-09 14:14:02 -080017package org.onosproject.routing.bgp;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080018
19import org.apache.commons.lang3.tuple.Pair;
20import org.jboss.netty.buffer.ChannelBuffer;
21import org.jboss.netty.buffer.ChannelBuffers;
22import org.jboss.netty.channel.ChannelHandlerContext;
23import org.onlab.packet.Ip4Address;
24import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080025import org.onlab.packet.Ip6Address;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080026import org.onlab.packet.Ip6Prefix;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080027import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
Jonathan Hart41349e92015-02-09 14:14:02 -080030import java.util.ArrayList;
31import java.util.Collection;
32import java.util.HashMap;
33import java.util.Map;
34
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080035/**
36 * A class for handling BGP UPDATE messages.
37 */
38final class BgpUpdate {
39 private static final Logger log = LoggerFactory.getLogger(BgpUpdate.class);
40
41 /**
42 * Default constructor.
43 * <p>
44 * The constructor is private to prevent creating an instance of
45 * this utility class.
46 */
47 private BgpUpdate() {
48 }
49
50 /**
51 * Processes BGP UPDATE message.
52 *
53 * @param bgpSession the BGP Session to use
54 * @param ctx the Channel Handler Context
55 * @param message the message to process
56 */
57 static void processBgpUpdate(BgpSession bgpSession,
58 ChannelHandlerContext ctx,
59 ChannelBuffer message) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080060 DecodedBgpRoutes decodedBgpRoutes = new DecodedBgpRoutes();
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080061
62 int minLength =
63 BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
64 if (message.readableBytes() < minLength) {
65 log.debug("BGP RX UPDATE Error from {}: " +
66 "Message length {} too short. Must be at least {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080067 bgpSession.remoteInfo().address(),
68 message.readableBytes(), minLength);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080069 //
70 // ERROR: Bad Message Length
71 //
72 // Send NOTIFICATION and close the connection
73 ChannelBuffer txMessage =
74 BgpNotification.prepareBgpNotificationBadMessageLength(
75 message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
76 ctx.getChannel().write(txMessage);
77 bgpSession.closeSession(ctx);
78 return;
79 }
80
81 log.debug("BGP RX UPDATE message from {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -080082 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080083
84 //
85 // Parse the UPDATE message
86 //
87
88 //
89 // Parse the Withdrawn Routes
90 //
91 int withdrawnRoutesLength = message.readUnsignedShort();
92 if (withdrawnRoutesLength > message.readableBytes()) {
93 // ERROR: Malformed Attribute List
94 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
95 return;
96 }
97 Collection<Ip4Prefix> withdrawnPrefixes = null;
98 try {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -080099 withdrawnPrefixes = parsePackedIp4Prefixes(withdrawnRoutesLength,
100 message);
Jonathan Hart41349e92015-02-09 14:14:02 -0800101 } catch (BgpMessage.BgpParseException e) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800102 // ERROR: Invalid Network Field
103 log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800104 bgpSession.remoteInfo().bgpId(), e);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800105 actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
106 return;
107 }
108 for (Ip4Prefix prefix : withdrawnPrefixes) {
109 log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800110 bgpSession.remoteInfo().address(), prefix);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800111 BgpRouteEntry bgpRouteEntry = bgpSession.findBgpRoute(prefix);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800112 if (bgpRouteEntry != null) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800113 decodedBgpRoutes.deletedUnicastRoutes4.put(prefix,
114 bgpRouteEntry);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800115 }
116 }
117
118 //
119 // Parse the Path Attributes
120 //
121 try {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800122 parsePathAttributes(bgpSession, ctx, message, decodedBgpRoutes);
Jonathan Hart41349e92015-02-09 14:14:02 -0800123 } catch (BgpMessage.BgpParseException e) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800124 log.debug("Exception parsing Path Attributes from BGP peer {}: ",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800125 bgpSession.remoteInfo().bgpId(), e);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800126 // NOTE: The session was already closed, so nothing else to do
127 return;
128 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800129
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800130 //
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800131 // Update the BGP RIB-IN
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800132 //
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800133 for (Ip4Prefix ip4Prefix :
134 decodedBgpRoutes.deletedUnicastRoutes4.keySet()) {
135 bgpSession.removeBgpRoute(ip4Prefix);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800136 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800137 //
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800138 for (BgpRouteEntry bgpRouteEntry :
139 decodedBgpRoutes.addedUnicastRoutes4.values()) {
140 bgpSession.addBgpRoute(bgpRouteEntry);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800141 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800142 //
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800143 for (Ip6Prefix ip6Prefix :
144 decodedBgpRoutes.deletedUnicastRoutes6.keySet()) {
145 bgpSession.removeBgpRoute(ip6Prefix);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800146 }
147 //
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800148 for (BgpRouteEntry bgpRouteEntry :
149 decodedBgpRoutes.addedUnicastRoutes6.values()) {
150 bgpSession.addBgpRoute(bgpRouteEntry);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800151 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800152
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800153 //
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800154 // Push the updates to the BGP Merged RIB
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800155 //
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800156 BgpRouteSelector bgpRouteSelector =
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800157 bgpSession.getBgpSessionManager().getBgpRouteSelector();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700158 bgpRouteSelector.routeUpdates(
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800159 decodedBgpRoutes.addedUnicastRoutes4.values(),
160 decodedBgpRoutes.deletedUnicastRoutes4.values());
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700161 bgpRouteSelector.routeUpdates(
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800162 decodedBgpRoutes.addedUnicastRoutes6.values(),
163 decodedBgpRoutes.deletedUnicastRoutes6.values());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800164
165 // Start the Session Timeout timer
166 bgpSession.restartSessionTimeoutTimer(ctx);
167 }
168
169 /**
170 * Parse BGP Path Attributes from the BGP UPDATE message.
171 *
172 * @param bgpSession the BGP Session to use
173 * @param ctx the Channel Handler Context
174 * @param message the message to parse
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800175 * @param decodedBgpRoutes the container to store the decoded BGP Route
176 * Entries. It might already contain some route entries such as withdrawn
177 * IPv4 prefixes
Jonathan Hart41349e92015-02-09 14:14:02 -0800178 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800179 */
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800180 // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES
181 private static void parsePathAttributes(
182 BgpSession bgpSession,
183 ChannelHandlerContext ctx,
184 ChannelBuffer message,
185 DecodedBgpRoutes decodedBgpRoutes)
Jonathan Hart41349e92015-02-09 14:14:02 -0800186 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800187
188 //
189 // Parsed values
190 //
191 Short origin = -1; // Mandatory
192 BgpRouteEntry.AsPath asPath = null; // Mandatory
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800193 // Legacy NLRI (RFC 4271). Mandatory NEXT_HOP if legacy NLRI is used
Jonathan Hart41349e92015-02-09 14:14:02 -0800194 MpNlri legacyNlri = new MpNlri(
195 BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4,
196 BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800197 long multiExitDisc = // Optional
Jonathan Hart41349e92015-02-09 14:14:02 -0800198 BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800199 Long localPref = null; // Mandatory
200 Long aggregatorAsNumber = null; // Optional: unused
201 Ip4Address aggregatorIpAddress = null; // Optional: unused
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800202 Collection<MpNlri> mpNlriReachList = new ArrayList<>(); // Optional
203 Collection<MpNlri> mpNlriUnreachList = new ArrayList<>(); // Optional
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800204
205 //
206 // Get and verify the Path Attributes Length
207 //
208 int pathAttributeLength = message.readUnsignedShort();
209 if (pathAttributeLength > message.readableBytes()) {
210 // ERROR: Malformed Attribute List
211 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
212 String errorMsg = "Malformed Attribute List";
Jonathan Hart41349e92015-02-09 14:14:02 -0800213 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800214 }
215 if (pathAttributeLength == 0) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800216 return;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800217 }
218
219 //
220 // Parse the Path Attributes
221 //
222 int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
223 while (message.readerIndex() < pathAttributeEnd) {
224 int attrFlags = message.readUnsignedByte();
225 if (message.readerIndex() >= pathAttributeEnd) {
226 // ERROR: Malformed Attribute List
227 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
228 String errorMsg = "Malformed Attribute List";
Jonathan Hart41349e92015-02-09 14:14:02 -0800229 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800230 }
231 int attrTypeCode = message.readUnsignedByte();
232
233 // The Attribute Flags
234 boolean optionalBit = ((0x80 & attrFlags) != 0);
235 boolean transitiveBit = ((0x40 & attrFlags) != 0);
236 boolean partialBit = ((0x20 & attrFlags) != 0);
237 boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
238
239 // The Attribute Length
240 int attrLen = 0;
241 int attrLenOctets = 1;
242 if (extendedLengthBit) {
243 attrLenOctets = 2;
244 }
245 if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
246 // ERROR: Malformed Attribute List
247 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
248 String errorMsg = "Malformed Attribute List";
Jonathan Hart41349e92015-02-09 14:14:02 -0800249 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800250 }
251 if (extendedLengthBit) {
252 attrLen = message.readUnsignedShort();
253 } else {
254 attrLen = message.readUnsignedByte();
255 }
256 if (message.readerIndex() + attrLen > pathAttributeEnd) {
257 // ERROR: Malformed Attribute List
258 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
259 String errorMsg = "Malformed Attribute List";
Jonathan Hart41349e92015-02-09 14:14:02 -0800260 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800261 }
262
263 // Verify the Attribute Flags
264 verifyBgpUpdateAttributeFlags(bgpSession, ctx, attrTypeCode,
265 attrLen, attrFlags, message);
266
267 //
268 // Extract the Attribute Value based on the Attribute Type Code
269 //
270 switch (attrTypeCode) {
271
Jonathan Hart41349e92015-02-09 14:14:02 -0800272 case BgpConstants.Update.Origin.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800273 // Attribute Type Code ORIGIN
274 origin = parseAttributeTypeOrigin(bgpSession, ctx,
275 attrTypeCode, attrLen,
276 attrFlags, message);
277 break;
278
Jonathan Hart41349e92015-02-09 14:14:02 -0800279 case BgpConstants.Update.AsPath.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800280 // Attribute Type Code AS_PATH
281 asPath = parseAttributeTypeAsPath(bgpSession, ctx,
282 attrTypeCode, attrLen,
283 attrFlags, message);
284 break;
285
Jonathan Hart41349e92015-02-09 14:14:02 -0800286 case BgpConstants.Update.NextHop.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800287 // Attribute Type Code NEXT_HOP
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800288 legacyNlri.nextHop4 =
289 parseAttributeTypeNextHop(bgpSession, ctx,
290 attrTypeCode, attrLen,
291 attrFlags, message);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800292 break;
293
Jonathan Hart41349e92015-02-09 14:14:02 -0800294 case BgpConstants.Update.MultiExitDisc.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800295 // Attribute Type Code MULTI_EXIT_DISC
296 multiExitDisc =
297 parseAttributeTypeMultiExitDisc(bgpSession, ctx,
298 attrTypeCode, attrLen,
299 attrFlags, message);
300 break;
301
Jonathan Hart41349e92015-02-09 14:14:02 -0800302 case BgpConstants.Update.LocalPref.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800303 // Attribute Type Code LOCAL_PREF
304 localPref =
305 parseAttributeTypeLocalPref(bgpSession, ctx,
306 attrTypeCode, attrLen,
307 attrFlags, message);
308 break;
309
Jonathan Hart41349e92015-02-09 14:14:02 -0800310 case BgpConstants.Update.AtomicAggregate.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800311 // Attribute Type Code ATOMIC_AGGREGATE
312 parseAttributeTypeAtomicAggregate(bgpSession, ctx,
313 attrTypeCode, attrLen,
314 attrFlags, message);
315 // Nothing to do: this attribute is primarily informational
316 break;
317
Jonathan Hart41349e92015-02-09 14:14:02 -0800318 case BgpConstants.Update.Aggregator.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800319 // Attribute Type Code AGGREGATOR
320 Pair<Long, Ip4Address> aggregator =
321 parseAttributeTypeAggregator(bgpSession, ctx,
322 attrTypeCode, attrLen,
323 attrFlags, message);
324 aggregatorAsNumber = aggregator.getLeft();
325 aggregatorIpAddress = aggregator.getRight();
326 break;
327
Jonathan Hart41349e92015-02-09 14:14:02 -0800328 case BgpConstants.Update.MpReachNlri.TYPE:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800329 // Attribute Type Code MP_REACH_NLRI
330 MpNlri mpNlriReach =
331 parseAttributeTypeMpReachNlri(bgpSession, ctx,
332 attrTypeCode,
333 attrLen,
334 attrFlags, message);
335 if (mpNlriReach != null) {
336 mpNlriReachList.add(mpNlriReach);
337 }
338 break;
339
Jonathan Hart41349e92015-02-09 14:14:02 -0800340 case BgpConstants.Update.MpUnreachNlri.TYPE:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800341 // Attribute Type Code MP_UNREACH_NLRI
342 MpNlri mpNlriUnreach =
343 parseAttributeTypeMpUnreachNlri(bgpSession, ctx,
344 attrTypeCode, attrLen,
345 attrFlags, message);
346 if (mpNlriUnreach != null) {
347 mpNlriUnreachList.add(mpNlriUnreach);
348 }
349 break;
350
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800351 default:
352 // NOTE: Parse any new Attribute Types if needed
353 if (!optionalBit) {
354 // ERROR: Unrecognized Well-known Attribute
355 actionsBgpUpdateUnrecognizedWellKnownAttribute(
356 bgpSession, ctx, attrTypeCode, attrLen, attrFlags,
357 message);
358 String errorMsg = "Unrecognized Well-known Attribute: " +
359 attrTypeCode;
Jonathan Hart41349e92015-02-09 14:14:02 -0800360 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800361 }
362
363 // Skip the data from the unrecognized attribute
364 log.debug("BGP RX UPDATE message from {}: " +
365 "Unrecognized Attribute Type {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800366 bgpSession.remoteInfo().address(), attrTypeCode);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800367 message.skipBytes(attrLen);
368 break;
369 }
370 }
371
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800372 //
373 // Parse the NLRI (Network Layer Reachability Information)
374 //
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800375 int nlriLength = message.readableBytes();
376 try {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800377 Collection<Ip4Prefix> addedPrefixes4 =
378 parsePackedIp4Prefixes(nlriLength, message);
379 // Store it inside the legacy NLRI wrapper
380 legacyNlri.nlri4 = addedPrefixes4;
Jonathan Hart41349e92015-02-09 14:14:02 -0800381 } catch (BgpMessage.BgpParseException e) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800382 // ERROR: Invalid Network Field
383 log.debug("Exception parsing NLRI from BGP peer {}: ",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800384 bgpSession.remoteInfo().bgpId(), e);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800385 actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
386 // Rethrow the exception
387 throw e;
388 }
389
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800390 // Verify the Well-known Attributes
391 verifyBgpUpdateWellKnownAttributes(bgpSession, ctx, origin, asPath,
392 localPref, legacyNlri,
393 mpNlriReachList);
394
395 //
396 // Generate the deleted routes
397 //
398 for (MpNlri mpNlri : mpNlriUnreachList) {
399 BgpRouteEntry bgpRouteEntry;
400
401 // The deleted IPv4 routes
402 for (Ip4Prefix prefix : mpNlri.nlri4) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800403 bgpRouteEntry = bgpSession.findBgpRoute(prefix);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800404 if (bgpRouteEntry != null) {
405 decodedBgpRoutes.deletedUnicastRoutes4.put(prefix,
406 bgpRouteEntry);
407 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800408 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800409
410 // The deleted IPv6 routes
411 for (Ip6Prefix prefix : mpNlri.nlri6) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800412 bgpRouteEntry = bgpSession.findBgpRoute(prefix);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800413 if (bgpRouteEntry != null) {
414 decodedBgpRoutes.deletedUnicastRoutes6.put(prefix,
415 bgpRouteEntry);
416 }
417 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800418 }
419
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800420 //
421 // Generate the added routes
422 //
423 mpNlriReachList.add(legacyNlri);
424 for (MpNlri mpNlri : mpNlriReachList) {
425 BgpRouteEntry bgpRouteEntry;
426
427 // The added IPv4 routes
428 for (Ip4Prefix prefix : mpNlri.nlri4) {
429 bgpRouteEntry =
430 new BgpRouteEntry(bgpSession, prefix, mpNlri.nextHop4,
431 origin.byteValue(), asPath, localPref);
432 bgpRouteEntry.setMultiExitDisc(multiExitDisc);
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800433 if (bgpRouteEntry.hasAsPathLoop(bgpSession.localInfo().asNumber())) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800434 log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
435 "nextHop {}: contains AS Path loop",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800436 bgpSession.remoteInfo().address(), prefix,
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800437 mpNlri.nextHop4);
438 continue;
439 } else {
440 log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800441 bgpSession.remoteInfo().address(), prefix,
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800442 mpNlri.nextHop4);
443 }
444 // Remove from the collection of deleted routes
445 decodedBgpRoutes.deletedUnicastRoutes4.remove(prefix);
446 decodedBgpRoutes.addedUnicastRoutes4.put(prefix,
447 bgpRouteEntry);
448 }
449
450 // The added IPv6 routes
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800451 for (Ip6Prefix prefix : mpNlri.nlri6) {
452 bgpRouteEntry =
453 new BgpRouteEntry(bgpSession, prefix, mpNlri.nextHop6,
454 origin.byteValue(), asPath, localPref);
455 bgpRouteEntry.setMultiExitDisc(multiExitDisc);
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800456 if (bgpRouteEntry.hasAsPathLoop(bgpSession.localInfo().asNumber())) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800457 log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
458 "nextHop {}: contains AS Path loop",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800459 bgpSession.remoteInfo().address(), prefix,
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800460 mpNlri.nextHop6);
461 continue;
462 } else {
463 log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800464 bgpSession.remoteInfo().address(), prefix,
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800465 mpNlri.nextHop6);
466 }
467 // Remove from the collection of deleted routes
468 decodedBgpRoutes.deletedUnicastRoutes6.remove(prefix);
469 decodedBgpRoutes.addedUnicastRoutes6.put(prefix,
470 bgpRouteEntry);
471 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800472 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800473 }
474
475 /**
476 * Verifies BGP UPDATE Well-known Attributes.
477 *
478 * @param bgpSession the BGP Session to use
479 * @param ctx the Channel Handler Context
480 * @param origin the ORIGIN well-known mandatory attribute
481 * @param asPath the AS_PATH well-known mandatory attribute
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800482 * @param localPref the LOCAL_PREF required attribute
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800483 * @param legacyNlri the legacy NLRI. Encapsulates the NEXT_HOP well-known
484 * mandatory attribute (mandatory if legacy NLRI is used).
485 * @param mpNlriReachList the Multiprotocol NLRI attributes
Jonathan Hart41349e92015-02-09 14:14:02 -0800486 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800487 */
488 private static void verifyBgpUpdateWellKnownAttributes(
489 BgpSession bgpSession,
490 ChannelHandlerContext ctx,
491 Short origin,
492 BgpRouteEntry.AsPath asPath,
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800493 Long localPref,
494 MpNlri legacyNlri,
495 Collection<MpNlri> mpNlriReachList)
Jonathan Hart41349e92015-02-09 14:14:02 -0800496 throws BgpMessage.BgpParseException {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800497 boolean hasNlri = false;
498 boolean hasLegacyNlri = false;
499
500 //
501 // Convenience flags that are used to check for missing attributes.
502 //
503 // NOTE: The hasLegacyNlri flag is always set to true if the
504 // Multiprotocol Extensions are not enabled, even if the UPDATE
505 // message doesn't contain the legacy NLRI (per RFC 4271).
506 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800507 if (!bgpSession.mpExtensions()) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800508 hasNlri = true;
509 hasLegacyNlri = true;
510 } else {
511 if (!legacyNlri.nlri4.isEmpty()) {
512 hasNlri = true;
513 hasLegacyNlri = true;
514 }
515 if (!mpNlriReachList.isEmpty()) {
516 hasNlri = true;
517 }
518 }
519
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800520 //
521 // Check for Missing Well-known Attributes
522 //
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800523 if (hasNlri && ((origin == null) || (origin == -1))) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800524 // Missing Attribute Type Code ORIGIN
Jonathan Hart41349e92015-02-09 14:14:02 -0800525 int type = BgpConstants.Update.Origin.TYPE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800526 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
527 String errorMsg = "Missing Well-known Attribute: ORIGIN";
Jonathan Hart41349e92015-02-09 14:14:02 -0800528 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800529 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800530 if (hasNlri && (asPath == null)) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800531 // Missing Attribute Type Code AS_PATH
Jonathan Hart41349e92015-02-09 14:14:02 -0800532 int type = BgpConstants.Update.AsPath.TYPE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800533 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
534 String errorMsg = "Missing Well-known Attribute: AS_PATH";
Jonathan Hart41349e92015-02-09 14:14:02 -0800535 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800536 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800537 if (hasNlri && (localPref == null)) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800538 // Missing Attribute Type Code LOCAL_PREF
539 // NOTE: Required for iBGP
Jonathan Hart41349e92015-02-09 14:14:02 -0800540 int type = BgpConstants.Update.LocalPref.TYPE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800541 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
542 String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
Jonathan Hart41349e92015-02-09 14:14:02 -0800543 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800544 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800545 if (hasLegacyNlri && (legacyNlri.nextHop4 == null)) {
546 // Missing Attribute Type Code NEXT_HOP
Jonathan Hart41349e92015-02-09 14:14:02 -0800547 int type = BgpConstants.Update.NextHop.TYPE;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800548 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
549 String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
Jonathan Hart41349e92015-02-09 14:14:02 -0800550 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800551 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800552 }
553
554 /**
555 * Verifies the BGP UPDATE Attribute Flags.
556 *
557 * @param bgpSession the BGP Session to use
558 * @param ctx the Channel Handler Context
559 * @param attrTypeCode the attribute type code
560 * @param attrLen the attribute length (in octets)
561 * @param attrFlags the attribute flags
562 * @param message the message to parse
Jonathan Hart41349e92015-02-09 14:14:02 -0800563 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800564 */
565 private static void verifyBgpUpdateAttributeFlags(
566 BgpSession bgpSession,
567 ChannelHandlerContext ctx,
568 int attrTypeCode,
569 int attrLen,
570 int attrFlags,
571 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800572 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800573
574 //
575 // Assign the Attribute Type Name and the Well-known flag
576 //
577 String typeName = "UNKNOWN";
578 boolean isWellKnown = false;
579 switch (attrTypeCode) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800580 case BgpConstants.Update.Origin.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800581 isWellKnown = true;
582 typeName = "ORIGIN";
583 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800584 case BgpConstants.Update.AsPath.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800585 isWellKnown = true;
586 typeName = "AS_PATH";
587 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800588 case BgpConstants.Update.NextHop.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800589 isWellKnown = true;
590 typeName = "NEXT_HOP";
591 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800592 case BgpConstants.Update.MultiExitDisc.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800593 isWellKnown = false;
594 typeName = "MULTI_EXIT_DISC";
595 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800596 case BgpConstants.Update.LocalPref.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800597 isWellKnown = true;
598 typeName = "LOCAL_PREF";
599 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800600 case BgpConstants.Update.AtomicAggregate.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800601 isWellKnown = true;
602 typeName = "ATOMIC_AGGREGATE";
603 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800604 case BgpConstants.Update.Aggregator.TYPE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800605 isWellKnown = false;
606 typeName = "AGGREGATOR";
607 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800608 case BgpConstants.Update.MpReachNlri.TYPE:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800609 isWellKnown = false;
610 typeName = "MP_REACH_NLRI";
611 break;
Jonathan Hart41349e92015-02-09 14:14:02 -0800612 case BgpConstants.Update.MpUnreachNlri.TYPE:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -0800613 isWellKnown = false;
614 typeName = "MP_UNREACH_NLRI";
615 break;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800616 default:
617 isWellKnown = false;
618 typeName = "UNKNOWN(" + attrTypeCode + ")";
619 break;
620 }
621
622 //
623 // Verify the Attribute Flags
624 //
625 boolean optionalBit = ((0x80 & attrFlags) != 0);
626 boolean transitiveBit = ((0x40 & attrFlags) != 0);
627 boolean partialBit = ((0x20 & attrFlags) != 0);
628 if ((isWellKnown && optionalBit) ||
629 (isWellKnown && (!transitiveBit)) ||
630 (isWellKnown && partialBit) ||
631 (optionalBit && (!transitiveBit) && partialBit)) {
632 //
633 // ERROR: The Optional bit cannot be set for Well-known attributes
634 // ERROR: The Transtive bit MUST be 1 for well-known attributes
635 // ERROR: The Partial bit MUST be 0 for well-known attributes
636 // ERROR: The Partial bit MUST be 0 for optional non-transitive
637 // attributes
638 //
639 actionsBgpUpdateAttributeFlagsError(
640 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
641 String errorMsg = "Attribute Flags Error for " + typeName + ": " +
642 attrFlags;
Jonathan Hart41349e92015-02-09 14:14:02 -0800643 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800644 }
645 }
646
647 /**
648 * Parses BGP UPDATE Attribute Type ORIGIN.
649 *
650 * @param bgpSession the BGP Session to use
651 * @param ctx the Channel Handler Context
652 * @param attrTypeCode the attribute type code
653 * @param attrLen the attribute length (in octets)
654 * @param attrFlags the attribute flags
655 * @param message the message to parse
656 * @return the parsed ORIGIN value
Jonathan Hart41349e92015-02-09 14:14:02 -0800657 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800658 */
659 private static short parseAttributeTypeOrigin(
660 BgpSession bgpSession,
661 ChannelHandlerContext ctx,
662 int attrTypeCode,
663 int attrLen,
664 int attrFlags,
665 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800666 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800667
668 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -0800669 if (attrLen != BgpConstants.Update.Origin.LENGTH) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800670 // ERROR: Attribute Length Error
671 actionsBgpUpdateAttributeLengthError(
672 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
673 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800674 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800675 }
676
677 message.markReaderIndex();
678 short origin = message.readUnsignedByte();
679 switch (origin) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800680 case BgpConstants.Update.Origin.IGP:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800681 // FALLTHROUGH
Jonathan Hart41349e92015-02-09 14:14:02 -0800682 case BgpConstants.Update.Origin.EGP:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800683 // FALLTHROUGH
Jonathan Hart41349e92015-02-09 14:14:02 -0800684 case BgpConstants.Update.Origin.INCOMPLETE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800685 break;
686 default:
687 // ERROR: Invalid ORIGIN Attribute
688 message.resetReaderIndex();
689 actionsBgpUpdateInvalidOriginAttribute(
690 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
691 origin);
692 String errorMsg = "Invalid ORIGIN Attribute: " + origin;
Jonathan Hart41349e92015-02-09 14:14:02 -0800693 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800694 }
695
696 return origin;
697 }
698
699 /**
700 * Parses BGP UPDATE Attribute AS Path.
701 *
702 * @param bgpSession the BGP Session to use
703 * @param ctx the Channel Handler Context
704 * @param attrTypeCode the attribute type code
705 * @param attrLen the attribute length (in octets)
706 * @param attrFlags the attribute flags
707 * @param message the message to parse
708 * @return the parsed AS Path
Jonathan Hart41349e92015-02-09 14:14:02 -0800709 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800710 */
711 private static BgpRouteEntry.AsPath parseAttributeTypeAsPath(
712 BgpSession bgpSession,
713 ChannelHandlerContext ctx,
714 int attrTypeCode,
715 int attrLen,
716 int attrFlags,
717 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800718 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800719 ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
720
721 //
722 // Parse the message
723 //
724 while (attrLen > 0) {
725 if (attrLen < 2) {
726 // ERROR: Malformed AS_PATH
727 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
728 String errorMsg = "Malformed AS Path";
Jonathan Hart41349e92015-02-09 14:14:02 -0800729 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800730 }
731 // Get the Path Segment Type and Length (in number of ASes)
732 short pathSegmentType = message.readUnsignedByte();
733 short pathSegmentLength = message.readUnsignedByte();
734 attrLen -= 2;
735
736 // Verify the Path Segment Type
737 switch (pathSegmentType) {
Jonathan Hart41349e92015-02-09 14:14:02 -0800738 case BgpConstants.Update.AsPath.AS_SET:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800739 // FALLTHROUGH
Jonathan Hart41349e92015-02-09 14:14:02 -0800740 case BgpConstants.Update.AsPath.AS_SEQUENCE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800741 // FALLTHROUGH
Jonathan Hart41349e92015-02-09 14:14:02 -0800742 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800743 // FALLTHROUGH
Jonathan Hart41349e92015-02-09 14:14:02 -0800744 case BgpConstants.Update.AsPath.AS_CONFED_SET:
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800745 break;
746 default:
747 // ERROR: Invalid Path Segment Type
748 //
749 // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
750 // for "Invalid Path Segment Type", hence we return
751 // the error as "Malformed AS_PATH".
752 //
753 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
754 String errorMsg =
755 "Invalid AS Path Segment Type: " + pathSegmentType;
Jonathan Hart41349e92015-02-09 14:14:02 -0800756 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800757 }
758
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900759 // 4-octet AS number handling.
760 int asPathLen;
761 if (bgpSession.isAs4OctetCapable()) {
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700762 asPathLen = BgpConstants.Update.AS_4OCTET_LENGTH;
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900763 } else {
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700764 asPathLen = BgpConstants.Update.AS_LENGTH;
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900765 }
766
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800767 // Parse the AS numbers
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900768 if (asPathLen * pathSegmentLength > attrLen) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800769 // ERROR: Malformed AS_PATH
770 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
771 String errorMsg = "Malformed AS Path";
Jonathan Hart41349e92015-02-09 14:14:02 -0800772 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800773 }
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900774 attrLen -= (asPathLen * pathSegmentLength);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800775 ArrayList<Long> segmentAsNumbers = new ArrayList<>();
776 while (pathSegmentLength-- > 0) {
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900777 long asNumber;
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700778 if (asPathLen == BgpConstants.Update.AS_4OCTET_LENGTH) {
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900779 asNumber = message.readUnsignedInt();
780 } else {
781 asNumber = message.readUnsignedShort();
782 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800783 segmentAsNumbers.add(asNumber);
784 }
785
786 BgpRouteEntry.PathSegment pathSegment =
787 new BgpRouteEntry.PathSegment((byte) pathSegmentType,
788 segmentAsNumbers);
789 pathSegments.add(pathSegment);
790 }
791
792 return new BgpRouteEntry.AsPath(pathSegments);
793 }
794
795 /**
796 * Parses BGP UPDATE Attribute Type NEXT_HOP.
797 *
798 * @param bgpSession the BGP Session to use
799 * @param ctx the Channel Handler Context
800 * @param attrTypeCode the attribute type code
801 * @param attrLen the attribute length (in octets)
802 * @param attrFlags the attribute flags
803 * @param message the message to parse
804 * @return the parsed NEXT_HOP value
Jonathan Hart41349e92015-02-09 14:14:02 -0800805 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800806 */
807 private static Ip4Address parseAttributeTypeNextHop(
808 BgpSession bgpSession,
809 ChannelHandlerContext ctx,
810 int attrTypeCode,
811 int attrLen,
812 int attrFlags,
813 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800814 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800815
816 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -0800817 if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800818 // ERROR: Attribute Length Error
819 actionsBgpUpdateAttributeLengthError(
820 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
821 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800822 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800823 }
824
825 message.markReaderIndex();
826 Ip4Address nextHopAddress =
827 Ip4Address.valueOf((int) message.readUnsignedInt());
828 //
829 // Check whether the NEXT_HOP IP address is semantically correct.
830 // As per RFC 4271, Section 6.3:
831 //
832 // a) It MUST NOT be the IP address of the receiving speaker
833 // b) In the case of an EBGP ....
834 //
835 // Here we check only (a), because (b) doesn't apply for us: all our
836 // peers are iBGP.
837 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800838 if (nextHopAddress.equals(bgpSession.localInfo().ip4Address())) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800839 // ERROR: Invalid NEXT_HOP Attribute
840 message.resetReaderIndex();
841 actionsBgpUpdateInvalidNextHopAttribute(
842 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
843 nextHopAddress);
844 String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
Jonathan Hart41349e92015-02-09 14:14:02 -0800845 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800846 }
847
848 return nextHopAddress;
849 }
850
851 /**
852 * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
853 *
854 * @param bgpSession the BGP Session to use
855 * @param ctx the Channel Handler Context
856 * @param attrTypeCode the attribute type code
857 * @param attrLen the attribute length (in octets)
858 * @param attrFlags the attribute flags
859 * @param message the message to parse
860 * @return the parsed MULTI_EXIT_DISC value
Jonathan Hart41349e92015-02-09 14:14:02 -0800861 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800862 */
863 private static long parseAttributeTypeMultiExitDisc(
864 BgpSession bgpSession,
865 ChannelHandlerContext ctx,
866 int attrTypeCode,
867 int attrLen,
868 int attrFlags,
869 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800870 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800871
872 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -0800873 if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800874 // ERROR: Attribute Length Error
875 actionsBgpUpdateAttributeLengthError(
876 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
877 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800878 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800879 }
880
881 long multiExitDisc = message.readUnsignedInt();
882 return multiExitDisc;
883 }
884
885 /**
886 * Parses BGP UPDATE Attribute Type LOCAL_PREF.
887 *
888 * @param bgpSession the BGP Session to use
889 * @param ctx the Channel Handler Context
890 * @param attrTypeCode the attribute type code
891 * @param attrLen the attribute length (in octets)
892 * @param attrFlags the attribute flags
893 * @param message the message to parse
894 * @return the parsed LOCAL_PREF value
Jonathan Hart41349e92015-02-09 14:14:02 -0800895 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800896 */
897 private static long parseAttributeTypeLocalPref(
898 BgpSession bgpSession,
899 ChannelHandlerContext ctx,
900 int attrTypeCode,
901 int attrLen,
902 int attrFlags,
903 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800904 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800905
906 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -0800907 if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800908 // ERROR: Attribute Length Error
909 actionsBgpUpdateAttributeLengthError(
910 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
911 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800912 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800913 }
914
915 long localPref = message.readUnsignedInt();
916 return localPref;
917 }
918
919 /**
920 * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
921 *
922 * @param bgpSession the BGP Session to use
923 * @param ctx the Channel Handler Context
924 * @param attrTypeCode the attribute type code
925 * @param attrLen the attribute length (in octets)
926 * @param attrFlags the attribute flags
927 * @param message the message to parse
Jonathan Hart41349e92015-02-09 14:14:02 -0800928 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800929 */
930 private static void parseAttributeTypeAtomicAggregate(
931 BgpSession bgpSession,
932 ChannelHandlerContext ctx,
933 int attrTypeCode,
934 int attrLen,
935 int attrFlags,
936 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800937 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800938
939 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -0800940 if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800941 // ERROR: Attribute Length Error
942 actionsBgpUpdateAttributeLengthError(
943 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
944 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800945 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800946 }
947
948 // Nothing to do: this attribute is primarily informational
949 }
950
951 /**
952 * Parses BGP UPDATE Attribute Type AGGREGATOR.
953 *
954 * @param bgpSession the BGP Session to use
955 * @param ctx the Channel Handler Context
956 * @param attrTypeCode the attribute type code
957 * @param attrLen the attribute length (in octets)
958 * @param attrFlags the attribute flags
959 * @param message the message to parse
960 * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
Jonathan Hart41349e92015-02-09 14:14:02 -0800961 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800962 */
963 private static Pair<Long, Ip4Address> parseAttributeTypeAggregator(
964 BgpSession bgpSession,
965 ChannelHandlerContext ctx,
966 int attrTypeCode,
967 int attrLen,
968 int attrFlags,
969 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -0800970 throws BgpMessage.BgpParseException {
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700971 int expectedAttrLen;
972
973 if (bgpSession.isAs4OctetCapable()) {
974 expectedAttrLen = BgpConstants.Update.Aggregator.AS4_LENGTH;
975 } else {
976 expectedAttrLen = BgpConstants.Update.Aggregator.AS2_LENGTH;
977 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800978
979 // Check the Attribute Length
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700980 if (attrLen != expectedAttrLen) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800981 // ERROR: Attribute Length Error
982 actionsBgpUpdateAttributeLengthError(
983 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
984 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -0800985 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800986 }
987
988 // The AGGREGATOR AS number
Pavlin Radoslavova460e292015-03-17 18:04:56 -0700989 long aggregatorAsNumber;
990 if (bgpSession.isAs4OctetCapable()) {
991 aggregatorAsNumber = message.readUnsignedInt();
992 } else {
993 aggregatorAsNumber = message.readUnsignedShort();
994 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800995 // The AGGREGATOR IP address
996 Ip4Address aggregatorIpAddress =
997 Ip4Address.valueOf((int) message.readUnsignedInt());
998
999 Pair<Long, Ip4Address> aggregator = Pair.of(aggregatorAsNumber,
1000 aggregatorIpAddress);
1001 return aggregator;
1002 }
1003
1004 /**
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001005 * Parses BGP UPDATE Attribute Type MP_REACH_NLRI.
1006 *
1007 * @param bgpSession the BGP Session to use
1008 * @param ctx the Channel Handler Context
1009 * @param attrTypeCode the attribute type code
1010 * @param attrLen the attribute length (in octets)
1011 * @param attrFlags the attribute flags
1012 * @param message the message to parse
1013 * @return the parsed MP_REACH_NLRI information if recognized, otherwise
1014 * null
Jonathan Hart41349e92015-02-09 14:14:02 -08001015 * @throws BgpMessage.BgpParseException
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001016 */
1017 private static MpNlri parseAttributeTypeMpReachNlri(
1018 BgpSession bgpSession,
1019 ChannelHandlerContext ctx,
1020 int attrTypeCode,
1021 int attrLen,
1022 int attrFlags,
1023 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -08001024 throws BgpMessage.BgpParseException {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001025 int attributeEnd = message.readerIndex() + attrLen;
1026
1027 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -08001028 if (attrLen < BgpConstants.Update.MpReachNlri.MIN_LENGTH) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001029 // ERROR: Attribute Length Error
1030 actionsBgpUpdateAttributeLengthError(
1031 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1032 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -08001033 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001034 }
1035
1036 message.markReaderIndex();
1037 int afi = message.readUnsignedShort();
1038 int safi = message.readUnsignedByte();
1039 int nextHopLen = message.readUnsignedByte();
1040
1041 //
1042 // Verify the AFI/SAFI, and skip the attribute if not recognized.
1043 // NOTE: Currently, we support only IPv4/IPv6 UNICAST
1044 //
Jonathan Hart41349e92015-02-09 14:14:02 -08001045 if (((afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4) &&
1046 (afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6)) ||
1047 (safi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST)) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001048 // Skip the attribute
1049 message.resetReaderIndex();
1050 message.skipBytes(attrLen);
1051 return null;
1052 }
1053
1054 //
1055 // Verify the next-hop length
1056 //
1057 int expectedNextHopLen = 0;
1058 switch (afi) {
Jonathan Hart41349e92015-02-09 14:14:02 -08001059 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001060 expectedNextHopLen = Ip4Address.BYTE_LENGTH;
1061 break;
Jonathan Hart41349e92015-02-09 14:14:02 -08001062 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001063 expectedNextHopLen = Ip6Address.BYTE_LENGTH;
1064 break;
1065 default:
1066 // UNREACHABLE
1067 break;
1068 }
1069 if (nextHopLen != expectedNextHopLen) {
1070 // ERROR: Optional Attribute Error
1071 message.resetReaderIndex();
1072 actionsBgpUpdateOptionalAttributeError(
1073 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1074 String errorMsg = "Invalid next-hop network address length. " +
1075 "Received " + nextHopLen + " expected " + expectedNextHopLen;
Jonathan Hart41349e92015-02-09 14:14:02 -08001076 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001077 }
1078 // NOTE: We use "+ 1" to take into account the Reserved field (1 octet)
1079 if (message.readerIndex() + nextHopLen + 1 >= attributeEnd) {
1080 // ERROR: Optional Attribute Error
1081 message.resetReaderIndex();
1082 actionsBgpUpdateOptionalAttributeError(
1083 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1084 String errorMsg = "Malformed next-hop network address";
Jonathan Hart41349e92015-02-09 14:14:02 -08001085 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001086 }
1087
1088 //
1089 // Get the Next-hop address, skip the Reserved field, and get the NLRI
1090 //
1091 byte[] nextHopBuffer = new byte[nextHopLen];
1092 message.readBytes(nextHopBuffer, 0, nextHopLen);
1093 int reserved = message.readUnsignedByte();
1094 MpNlri mpNlri = new MpNlri(afi, safi);
1095 try {
1096 switch (afi) {
Jonathan Hart41349e92015-02-09 14:14:02 -08001097 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001098 // The next-hop address
1099 mpNlri.nextHop4 = Ip4Address.valueOf(nextHopBuffer);
1100 // The NLRI
1101 mpNlri.nlri4 = parsePackedIp4Prefixes(
1102 attributeEnd - message.readerIndex(),
1103 message);
1104 break;
Jonathan Hart41349e92015-02-09 14:14:02 -08001105 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001106 // The next-hop address
1107 mpNlri.nextHop6 = Ip6Address.valueOf(nextHopBuffer);
1108 // The NLRI
1109 mpNlri.nlri6 = parsePackedIp6Prefixes(
1110 attributeEnd - message.readerIndex(),
1111 message);
1112 break;
1113 default:
1114 // UNREACHABLE
1115 break;
1116 }
Jonathan Hart41349e92015-02-09 14:14:02 -08001117 } catch (BgpMessage.BgpParseException e) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001118 // ERROR: Optional Attribute Error
1119 message.resetReaderIndex();
1120 actionsBgpUpdateOptionalAttributeError(
1121 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1122 String errorMsg = "Malformed network layer reachability information";
Jonathan Hart41349e92015-02-09 14:14:02 -08001123 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001124 }
1125
1126 return mpNlri;
1127 }
1128
1129 /**
1130 * Parses BGP UPDATE Attribute Type MP_UNREACH_NLRI.
1131 *
1132 * @param bgpSession the BGP Session to use
1133 * @param ctx the Channel Handler Context
1134 * @param attrTypeCode the attribute type code
1135 * @param attrLen the attribute length (in octets)
1136 * @param attrFlags the attribute flags
1137 * @param message the message to parse
1138 * @return the parsed MP_UNREACH_NLRI information if recognized, otherwise
1139 * null
Jonathan Hart41349e92015-02-09 14:14:02 -08001140 * @throws BgpMessage.BgpParseException
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001141 */
1142 private static MpNlri parseAttributeTypeMpUnreachNlri(
1143 BgpSession bgpSession,
1144 ChannelHandlerContext ctx,
1145 int attrTypeCode,
1146 int attrLen,
1147 int attrFlags,
1148 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -08001149 throws BgpMessage.BgpParseException {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001150 int attributeEnd = message.readerIndex() + attrLen;
1151
1152 // Check the Attribute Length
Jonathan Hart41349e92015-02-09 14:14:02 -08001153 if (attrLen < BgpConstants.Update.MpUnreachNlri.MIN_LENGTH) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001154 // ERROR: Attribute Length Error
1155 actionsBgpUpdateAttributeLengthError(
1156 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1157 String errorMsg = "Attribute Length Error";
Jonathan Hart41349e92015-02-09 14:14:02 -08001158 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001159 }
1160
1161 message.markReaderIndex();
1162 int afi = message.readUnsignedShort();
1163 int safi = message.readUnsignedByte();
1164
1165 //
1166 // Verify the AFI/SAFI, and skip the attribute if not recognized.
1167 // NOTE: Currently, we support only IPv4/IPv6 UNICAST
1168 //
Jonathan Hart41349e92015-02-09 14:14:02 -08001169 if (((afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4) &&
1170 (afi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6)) ||
1171 (safi != BgpConstants.Open.Capabilities.MultiprotocolExtensions.SAFI_UNICAST)) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001172 // Skip the attribute
1173 message.resetReaderIndex();
1174 message.skipBytes(attrLen);
1175 return null;
1176 }
1177
1178 //
1179 // Get the Withdrawn Routes
1180 //
1181 MpNlri mpNlri = new MpNlri(afi, safi);
1182 try {
1183 switch (afi) {
Jonathan Hart41349e92015-02-09 14:14:02 -08001184 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV4:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001185 // The Withdrawn Routes
1186 mpNlri.nlri4 = parsePackedIp4Prefixes(
1187 attributeEnd - message.readerIndex(),
1188 message);
1189 break;
Jonathan Hart41349e92015-02-09 14:14:02 -08001190 case BgpConstants.Open.Capabilities.MultiprotocolExtensions.AFI_IPV6:
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001191 // The Withdrawn Routes
1192 mpNlri.nlri6 = parsePackedIp6Prefixes(
1193 attributeEnd - message.readerIndex(),
1194 message);
1195 break;
1196 default:
1197 // UNREACHABLE
1198 break;
1199 }
Jonathan Hart41349e92015-02-09 14:14:02 -08001200 } catch (BgpMessage.BgpParseException e) {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001201 // ERROR: Optional Attribute Error
1202 message.resetReaderIndex();
1203 actionsBgpUpdateOptionalAttributeError(
1204 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
1205 String errorMsg = "Malformed withdrawn routes";
Jonathan Hart41349e92015-02-09 14:14:02 -08001206 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001207 }
1208
1209 return mpNlri;
1210 }
1211
1212 /**
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001213 * Parses a message that contains encoded IPv4 network prefixes.
1214 * <p>
1215 * The IPv4 prefixes are encoded in the form:
1216 * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
1217 * and Prefix is the IPv4 prefix (padded with trailing bits to the end
1218 * of an octet).
1219 *
1220 * @param totalLength the total length of the data to parse
1221 * @param message the message with data to parse
1222 * @return a collection of parsed IPv4 network prefixes
Jonathan Hart41349e92015-02-09 14:14:02 -08001223 * @throws BgpMessage.BgpParseException
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001224 */
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001225 private static Collection<Ip4Prefix> parsePackedIp4Prefixes(
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001226 int totalLength,
1227 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -08001228 throws BgpMessage.BgpParseException {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001229 Collection<Ip4Prefix> result = new ArrayList<>();
1230
1231 if (totalLength == 0) {
1232 return result;
1233 }
1234
1235 // Parse the data
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001236 byte[] buffer = new byte[Ip4Address.BYTE_LENGTH];
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001237 int dataEnd = message.readerIndex() + totalLength;
1238 while (message.readerIndex() < dataEnd) {
1239 int prefixBitlen = message.readUnsignedByte();
1240 int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
1241 if (message.readerIndex() + prefixBytelen > dataEnd) {
1242 String errorMsg = "Malformed Network Prefixes";
Jonathan Hart41349e92015-02-09 14:14:02 -08001243 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001244 }
1245
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001246 message.readBytes(buffer, 0, prefixBytelen);
1247 Ip4Prefix prefix = Ip4Prefix.valueOf(Ip4Address.valueOf(buffer),
1248 prefixBitlen);
1249 result.add(prefix);
1250 }
1251
1252 return result;
1253 }
1254
1255 /**
1256 * Parses a message that contains encoded IPv6 network prefixes.
1257 * <p>
1258 * The IPv6 prefixes are encoded in the form:
1259 * <Length, Prefix> where Length is the length in bits of the IPv6 prefix,
1260 * and Prefix is the IPv6 prefix (padded with trailing bits to the end
1261 * of an octet).
1262 *
1263 * @param totalLength the total length of the data to parse
1264 * @param message the message with data to parse
1265 * @return a collection of parsed IPv6 network prefixes
Jonathan Hart41349e92015-02-09 14:14:02 -08001266 * @throws BgpMessage.BgpParseException
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001267 */
1268 private static Collection<Ip6Prefix> parsePackedIp6Prefixes(
1269 int totalLength,
1270 ChannelBuffer message)
Jonathan Hart41349e92015-02-09 14:14:02 -08001271 throws BgpMessage.BgpParseException {
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001272 Collection<Ip6Prefix> result = new ArrayList<>();
1273
1274 if (totalLength == 0) {
1275 return result;
1276 }
1277
1278 // Parse the data
1279 byte[] buffer = new byte[Ip6Address.BYTE_LENGTH];
1280 int dataEnd = message.readerIndex() + totalLength;
1281 while (message.readerIndex() < dataEnd) {
1282 int prefixBitlen = message.readUnsignedByte();
1283 int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
1284 if (message.readerIndex() + prefixBytelen > dataEnd) {
1285 String errorMsg = "Malformed Network Prefixes";
Jonathan Hart41349e92015-02-09 14:14:02 -08001286 throw new BgpMessage.BgpParseException(errorMsg);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001287 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001288
1289 message.readBytes(buffer, 0, prefixBytelen);
1290 Ip6Prefix prefix = Ip6Prefix.valueOf(Ip6Address.valueOf(buffer),
1291 prefixBitlen);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001292 result.add(prefix);
1293 }
1294
1295 return result;
1296 }
1297
1298 /**
1299 * Applies the appropriate actions after detecting BGP UPDATE
1300 * Invalid Network Field Error: send NOTIFICATION and close the channel.
1301 *
1302 * @param bgpSession the BGP Session to use
1303 * @param ctx the Channel Handler Context
1304 */
1305 private static void actionsBgpUpdateInvalidNetworkField(
1306 BgpSession bgpSession,
1307 ChannelHandlerContext ctx) {
1308 log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001309 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001310
1311 //
1312 // ERROR: Invalid Network Field
1313 //
1314 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001315 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1316 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_NETWORK_FIELD;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001317 ChannelBuffer txMessage =
1318 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1319 null);
1320 ctx.getChannel().write(txMessage);
1321 bgpSession.closeSession(ctx);
1322 }
1323
1324 /**
1325 * Applies the appropriate actions after detecting BGP UPDATE
1326 * Malformed Attribute List Error: send NOTIFICATION and close the channel.
1327 *
1328 * @param bgpSession the BGP Session to use
1329 * @param ctx the Channel Handler Context
1330 */
1331 private static void actionsBgpUpdateMalformedAttributeList(
1332 BgpSession bgpSession,
1333 ChannelHandlerContext ctx) {
1334 log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001335 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001336
1337 //
1338 // ERROR: Malformed Attribute List
1339 //
1340 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001341 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1342 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001343 ChannelBuffer txMessage =
1344 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1345 null);
1346 ctx.getChannel().write(txMessage);
1347 bgpSession.closeSession(ctx);
1348 }
1349
1350 /**
1351 * Applies the appropriate actions after detecting BGP UPDATE
1352 * Missing Well-known Attribute Error: send NOTIFICATION and close the
1353 * channel.
1354 *
1355 * @param bgpSession the BGP Session to use
1356 * @param ctx the Channel Handler Context
1357 * @param missingAttrTypeCode the missing attribute type code
1358 */
1359 private static void actionsBgpUpdateMissingWellKnownAttribute(
1360 BgpSession bgpSession,
1361 ChannelHandlerContext ctx,
1362 int missingAttrTypeCode) {
1363 log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001364 bgpSession.remoteInfo().address(), missingAttrTypeCode);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001365
1366 //
1367 // ERROR: Missing Well-known Attribute
1368 //
1369 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001370 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1371 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001372 ChannelBuffer data = ChannelBuffers.buffer(1);
1373 data.writeByte(missingAttrTypeCode);
1374 ChannelBuffer txMessage =
1375 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1376 data);
1377 ctx.getChannel().write(txMessage);
1378 bgpSession.closeSession(ctx);
1379 }
1380
1381 /**
1382 * Applies the appropriate actions after detecting BGP UPDATE
1383 * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
1384 *
1385 * @param bgpSession the BGP Session to use
1386 * @param ctx the Channel Handler Context
1387 * @param attrTypeCode the attribute type code
1388 * @param attrLen the attribute length (in octets)
1389 * @param attrFlags the attribute flags
1390 * @param message the message with the data
1391 * @param origin the ORIGIN attribute value
1392 */
1393 private static void actionsBgpUpdateInvalidOriginAttribute(
1394 BgpSession bgpSession,
1395 ChannelHandlerContext ctx,
1396 int attrTypeCode,
1397 int attrLen,
1398 int attrFlags,
1399 ChannelBuffer message,
1400 short origin) {
1401 log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001402 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001403
1404 //
1405 // ERROR: Invalid ORIGIN Attribute
1406 //
1407 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001408 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1409 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001410 ChannelBuffer data =
1411 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1412 attrFlags, message);
1413 ChannelBuffer txMessage =
1414 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1415 data);
1416 ctx.getChannel().write(txMessage);
1417 bgpSession.closeSession(ctx);
1418 }
1419
1420 /**
1421 * Applies the appropriate actions after detecting BGP UPDATE
1422 * Attribute Flags Error: send NOTIFICATION and close the channel.
1423 *
1424 * @param bgpSession the BGP Session to use
1425 * @param ctx the Channel Handler Context
1426 * @param attrTypeCode the attribute type code
1427 * @param attrLen the attribute length (in octets)
1428 * @param attrFlags the attribute flags
1429 * @param message the message with the data
1430 */
1431 private static void actionsBgpUpdateAttributeFlagsError(
1432 BgpSession bgpSession,
1433 ChannelHandlerContext ctx,
1434 int attrTypeCode,
1435 int attrLen,
1436 int attrFlags,
1437 ChannelBuffer message) {
1438 log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001439 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001440
1441 //
1442 // ERROR: Attribute Flags Error
1443 //
1444 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001445 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1446 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001447 ChannelBuffer data =
1448 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1449 attrFlags, message);
1450 ChannelBuffer txMessage =
1451 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1452 data);
1453 ctx.getChannel().write(txMessage);
1454 bgpSession.closeSession(ctx);
1455 }
1456
1457 /**
1458 * Applies the appropriate actions after detecting BGP UPDATE
1459 * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
1460 * channel.
1461 *
1462 * @param bgpSession the BGP Session to use
1463 * @param ctx the Channel Handler Context
1464 * @param attrTypeCode the attribute type code
1465 * @param attrLen the attribute length (in octets)
1466 * @param attrFlags the attribute flags
1467 * @param message the message with the data
1468 * @param nextHop the NEXT_HOP attribute value
1469 */
1470 private static void actionsBgpUpdateInvalidNextHopAttribute(
1471 BgpSession bgpSession,
1472 ChannelHandlerContext ctx,
1473 int attrTypeCode,
1474 int attrLen,
1475 int attrFlags,
1476 ChannelBuffer message,
1477 Ip4Address nextHop) {
1478 log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001479 bgpSession.remoteInfo().address(), nextHop);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001480
1481 //
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001482 // ERROR: Invalid NEXT_HOP Attribute
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001483 //
1484 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001485 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1486 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001487 ChannelBuffer data =
1488 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1489 attrFlags, message);
1490 ChannelBuffer txMessage =
1491 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1492 data);
1493 ctx.getChannel().write(txMessage);
1494 bgpSession.closeSession(ctx);
1495 }
1496
1497 /**
1498 * Applies the appropriate actions after detecting BGP UPDATE
1499 * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
1500 * the channel.
1501 *
1502 * @param bgpSession the BGP Session to use
1503 * @param ctx the Channel Handler Context
1504 * @param attrTypeCode the attribute type code
1505 * @param attrLen the attribute length (in octets)
1506 * @param attrFlags the attribute flags
1507 * @param message the message with the data
1508 */
1509 private static void actionsBgpUpdateUnrecognizedWellKnownAttribute(
1510 BgpSession bgpSession,
1511 ChannelHandlerContext ctx,
1512 int attrTypeCode,
1513 int attrLen,
1514 int attrFlags,
1515 ChannelBuffer message) {
1516 log.debug("BGP RX UPDATE Error from {}: " +
1517 "Unrecognized Well-known Attribute Error: {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001518 bgpSession.remoteInfo().address(), attrTypeCode);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001519
1520 //
1521 // ERROR: Unrecognized Well-known Attribute
1522 //
1523 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001524 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001525 int errorSubcode =
Jonathan Hart41349e92015-02-09 14:14:02 -08001526 BgpConstants.Notifications.UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001527 ChannelBuffer data =
1528 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1529 attrFlags, message);
1530 ChannelBuffer txMessage =
1531 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1532 data);
1533 ctx.getChannel().write(txMessage);
1534 bgpSession.closeSession(ctx);
1535 }
1536
1537 /**
1538 * Applies the appropriate actions after detecting BGP UPDATE
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001539 * Optional Attribute Error: send NOTIFICATION and close
1540 * the channel.
1541 *
1542 * @param bgpSession the BGP Session to use
1543 * @param ctx the Channel Handler Context
1544 * @param attrTypeCode the attribute type code
1545 * @param attrLen the attribute length (in octets)
1546 * @param attrFlags the attribute flags
1547 * @param message the message with the data
1548 */
1549 private static void actionsBgpUpdateOptionalAttributeError(
1550 BgpSession bgpSession,
1551 ChannelHandlerContext ctx,
1552 int attrTypeCode,
1553 int attrLen,
1554 int attrFlags,
1555 ChannelBuffer message) {
1556 log.debug("BGP RX UPDATE Error from {}: Optional Attribute Error: {}",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001557 bgpSession.remoteInfo().address(), attrTypeCode);
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001558
1559 //
1560 // ERROR: Optional Attribute Error
1561 //
1562 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001563 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001564 int errorSubcode =
Jonathan Hart41349e92015-02-09 14:14:02 -08001565 BgpConstants.Notifications.UpdateMessageError.OPTIONAL_ATTRIBUTE_ERROR;
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001566 ChannelBuffer data =
1567 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1568 attrFlags, message);
1569 ChannelBuffer txMessage =
1570 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1571 data);
1572 ctx.getChannel().write(txMessage);
1573 bgpSession.closeSession(ctx);
1574 }
1575
1576 /**
1577 * Applies the appropriate actions after detecting BGP UPDATE
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001578 * Attribute Length Error: send NOTIFICATION and close the channel.
1579 *
1580 * @param bgpSession the BGP Session to use
1581 * @param ctx the Channel Handler Context
1582 * @param attrTypeCode the attribute type code
1583 * @param attrLen the attribute length (in octets)
1584 * @param attrFlags the attribute flags
1585 * @param message the message with the data
1586 */
1587 private static void actionsBgpUpdateAttributeLengthError(
1588 BgpSession bgpSession,
1589 ChannelHandlerContext ctx,
1590 int attrTypeCode,
1591 int attrLen,
1592 int attrFlags,
1593 ChannelBuffer message) {
1594 log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001595 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001596
1597 //
1598 // ERROR: Attribute Length Error
1599 //
1600 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001601 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1602 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001603 ChannelBuffer data =
1604 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1605 attrFlags, message);
1606 ChannelBuffer txMessage =
1607 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1608 data);
1609 ctx.getChannel().write(txMessage);
1610 bgpSession.closeSession(ctx);
1611 }
1612
1613 /**
1614 * Applies the appropriate actions after detecting BGP UPDATE
1615 * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
1616 *
1617 * @param bgpSession the BGP Session to use
1618 * @param ctx the Channel Handler Context
1619 */
1620 private static void actionsBgpUpdateMalformedAsPath(
1621 BgpSession bgpSession,
1622 ChannelHandlerContext ctx) {
1623 log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -08001624 bgpSession.remoteInfo().address());
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001625
1626 //
1627 // ERROR: Malformed AS_PATH
1628 //
1629 // Send NOTIFICATION and close the connection
Jonathan Hart41349e92015-02-09 14:14:02 -08001630 int errorCode = BgpConstants.Notifications.UpdateMessageError.ERROR_CODE;
1631 int errorSubcode = BgpConstants.Notifications.UpdateMessageError.MALFORMED_AS_PATH;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001632 ChannelBuffer txMessage =
1633 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1634 null);
1635 ctx.getChannel().write(txMessage);
1636 bgpSession.closeSession(ctx);
1637 }
1638
1639 /**
1640 * Prepares BGP UPDATE Notification data payload.
1641 *
1642 * @param attrTypeCode the attribute type code
1643 * @param attrLen the attribute length (in octets)
1644 * @param attrFlags the attribute flags
1645 * @param message the message with the data
1646 * @return the buffer with the data payload for the BGP UPDATE Notification
1647 */
1648 private static ChannelBuffer prepareBgpUpdateNotificationDataPayload(
1649 int attrTypeCode,
1650 int attrLen,
1651 int attrFlags,
1652 ChannelBuffer message) {
1653 // Compute the attribute length field octets
1654 boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
1655 int attrLenOctets = 1;
1656 if (extendedLengthBit) {
1657 attrLenOctets = 2;
1658 }
1659 ChannelBuffer data =
1660 ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
1661 data.writeByte(attrTypeCode);
1662 if (extendedLengthBit) {
1663 data.writeShort(attrLen);
1664 } else {
1665 data.writeByte(attrLen);
1666 }
1667 data.writeBytes(message, attrLen);
1668 return data;
1669 }
Pavlin Radoslavovc7648ee2014-12-19 16:20:33 -08001670
1671 /**
1672 * Helper class for storing Multiprotocol Network Layer Reachability
1673 * information.
1674 */
1675 private static final class MpNlri {
1676 private final int afi;
1677 private final int safi;
1678 private Ip4Address nextHop4;
1679 private Ip6Address nextHop6;
1680 private Collection<Ip4Prefix> nlri4 = new ArrayList<>();
1681 private Collection<Ip6Prefix> nlri6 = new ArrayList<>();
1682
1683 /**
1684 * Constructor.
1685 *
1686 * @param afi the Address Family Identifier
1687 * @param safi the Subsequent Address Family Identifier
1688 */
1689 private MpNlri(int afi, int safi) {
1690 this.afi = afi;
1691 this.safi = safi;
1692 }
1693 }
1694
1695 /**
1696 * Helper class for storing decoded BGP routing information.
1697 */
1698 private static final class DecodedBgpRoutes {
1699 private final Map<Ip4Prefix, BgpRouteEntry> addedUnicastRoutes4 =
1700 new HashMap<>();
1701 private final Map<Ip6Prefix, BgpRouteEntry> addedUnicastRoutes6 =
1702 new HashMap<>();
1703 private final Map<Ip4Prefix, BgpRouteEntry> deletedUnicastRoutes4 =
1704 new HashMap<>();
1705 private final Map<Ip6Prefix, BgpRouteEntry> deletedUnicastRoutes6 =
1706 new HashMap<>();
1707 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001708}