blob: f7bfa3241794d8bad300fdeb5c98f02e00bedfff [file] [log] [blame]
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
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 */
16package org.onosproject.sdnip.bgp;
17
18import java.util.ArrayList;
19import java.util.Collection;
20import java.util.HashMap;
21import java.util.Map;
22
23import org.apache.commons.lang3.tuple.Pair;
24import org.jboss.netty.buffer.ChannelBuffer;
25import org.jboss.netty.buffer.ChannelBuffers;
26import org.jboss.netty.channel.ChannelHandlerContext;
27import org.onlab.packet.Ip4Address;
28import org.onlab.packet.Ip4Prefix;
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +090029import org.onosproject.sdnip.bgp.BgpConstants.Update.AsPath;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080030import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
Pavlin Radoslavov278cdde2014-12-16 14:09:31 -080031import org.onosproject.sdnip.bgp.BgpMessage.BgpParseException;
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -080032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
35/**
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) {
60 Collection<BgpRouteEntry> addedRoutes = null;
61 Map<Ip4Prefix, BgpRouteEntry> deletedRoutes = new HashMap<>();
62
63 int minLength =
64 BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
65 if (message.readableBytes() < minLength) {
66 log.debug("BGP RX UPDATE Error from {}: " +
67 "Message length {} too short. Must be at least {}",
68 bgpSession.getRemoteAddress(), message.readableBytes(),
69 minLength);
70 //
71 // ERROR: Bad Message Length
72 //
73 // Send NOTIFICATION and close the connection
74 ChannelBuffer txMessage =
75 BgpNotification.prepareBgpNotificationBadMessageLength(
76 message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
77 ctx.getChannel().write(txMessage);
78 bgpSession.closeSession(ctx);
79 return;
80 }
81
82 log.debug("BGP RX UPDATE message from {}",
83 bgpSession.getRemoteAddress());
84
85 //
86 // Parse the UPDATE message
87 //
88
89 //
90 // Parse the Withdrawn Routes
91 //
92 int withdrawnRoutesLength = message.readUnsignedShort();
93 if (withdrawnRoutesLength > message.readableBytes()) {
94 // ERROR: Malformed Attribute List
95 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
96 return;
97 }
98 Collection<Ip4Prefix> withdrawnPrefixes = null;
99 try {
100 withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength,
101 message);
102 } catch (BgpParseException e) {
103 // ERROR: Invalid Network Field
104 log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
105 bgpSession.getRemoteBgpId(), e);
106 actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
107 return;
108 }
109 for (Ip4Prefix prefix : withdrawnPrefixes) {
110 log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
111 bgpSession.getRemoteAddress(), prefix);
112 BgpRouteEntry bgpRouteEntry = bgpSession.bgpRibIn().get(prefix);
113 if (bgpRouteEntry != null) {
114 deletedRoutes.put(prefix, bgpRouteEntry);
115 }
116 }
117
118 //
119 // Parse the Path Attributes
120 //
121 try {
122 addedRoutes = parsePathAttributes(bgpSession, ctx, message);
123 } catch (BgpParseException e) {
124 log.debug("Exception parsing Path Attributes from BGP peer {}: ",
125 bgpSession.getRemoteBgpId(), e);
126 // NOTE: The session was already closed, so nothing else to do
127 return;
128 }
129 // Ignore WITHDRAWN routes that are ADDED
130 for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
131 deletedRoutes.remove(bgpRouteEntry.prefix());
132 }
133
134 // Update the BGP RIB-IN
135 for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) {
136 bgpSession.bgpRibIn().remove(bgpRouteEntry.prefix());
137 }
138 for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
139 bgpSession.bgpRibIn().put(bgpRouteEntry.prefix(), bgpRouteEntry);
140 }
141
142 // Push the updates to the BGP Merged RIB
143 BgpSessionManager.BgpRouteSelector bgpRouteSelector =
144 bgpSession.getBgpSessionManager().getBgpRouteSelector();
145 bgpRouteSelector.routeUpdates(bgpSession, addedRoutes,
146 deletedRoutes.values());
147
148 // Start the Session Timeout timer
149 bgpSession.restartSessionTimeoutTimer(ctx);
150 }
151
152 /**
153 * Parse BGP Path Attributes from the BGP UPDATE message.
154 *
155 * @param bgpSession the BGP Session to use
156 * @param ctx the Channel Handler Context
157 * @param message the message to parse
158 * @return a collection of the result BGP Route Entries
159 * @throws BgpParseException
160 */
161 private static Collection<BgpRouteEntry> parsePathAttributes(
162 BgpSession bgpSession,
163 ChannelHandlerContext ctx,
164 ChannelBuffer message)
165 throws BgpParseException {
166 Map<Ip4Prefix, BgpRouteEntry> addedRoutes = new HashMap<>();
167
168 //
169 // Parsed values
170 //
171 Short origin = -1; // Mandatory
172 BgpRouteEntry.AsPath asPath = null; // Mandatory
173 Ip4Address nextHop = null; // Mandatory
174 long multiExitDisc = // Optional
175 BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
176 Long localPref = null; // Mandatory
177 Long aggregatorAsNumber = null; // Optional: unused
178 Ip4Address aggregatorIpAddress = null; // Optional: unused
179
180 //
181 // Get and verify the Path Attributes Length
182 //
183 int pathAttributeLength = message.readUnsignedShort();
184 if (pathAttributeLength > message.readableBytes()) {
185 // ERROR: Malformed Attribute List
186 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
187 String errorMsg = "Malformed Attribute List";
188 throw new BgpParseException(errorMsg);
189 }
190 if (pathAttributeLength == 0) {
191 return addedRoutes.values();
192 }
193
194 //
195 // Parse the Path Attributes
196 //
197 int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
198 while (message.readerIndex() < pathAttributeEnd) {
199 int attrFlags = message.readUnsignedByte();
200 if (message.readerIndex() >= pathAttributeEnd) {
201 // ERROR: Malformed Attribute List
202 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
203 String errorMsg = "Malformed Attribute List";
204 throw new BgpParseException(errorMsg);
205 }
206 int attrTypeCode = message.readUnsignedByte();
207
208 // The Attribute Flags
209 boolean optionalBit = ((0x80 & attrFlags) != 0);
210 boolean transitiveBit = ((0x40 & attrFlags) != 0);
211 boolean partialBit = ((0x20 & attrFlags) != 0);
212 boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
213
214 // The Attribute Length
215 int attrLen = 0;
216 int attrLenOctets = 1;
217 if (extendedLengthBit) {
218 attrLenOctets = 2;
219 }
220 if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
221 // ERROR: Malformed Attribute List
222 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
223 String errorMsg = "Malformed Attribute List";
224 throw new BgpParseException(errorMsg);
225 }
226 if (extendedLengthBit) {
227 attrLen = message.readUnsignedShort();
228 } else {
229 attrLen = message.readUnsignedByte();
230 }
231 if (message.readerIndex() + attrLen > pathAttributeEnd) {
232 // ERROR: Malformed Attribute List
233 actionsBgpUpdateMalformedAttributeList(bgpSession, ctx);
234 String errorMsg = "Malformed Attribute List";
235 throw new BgpParseException(errorMsg);
236 }
237
238 // Verify the Attribute Flags
239 verifyBgpUpdateAttributeFlags(bgpSession, ctx, attrTypeCode,
240 attrLen, attrFlags, message);
241
242 //
243 // Extract the Attribute Value based on the Attribute Type Code
244 //
245 switch (attrTypeCode) {
246
247 case BgpConstants.Update.Origin.TYPE:
248 // Attribute Type Code ORIGIN
249 origin = parseAttributeTypeOrigin(bgpSession, ctx,
250 attrTypeCode, attrLen,
251 attrFlags, message);
252 break;
253
254 case BgpConstants.Update.AsPath.TYPE:
255 // Attribute Type Code AS_PATH
256 asPath = parseAttributeTypeAsPath(bgpSession, ctx,
257 attrTypeCode, attrLen,
258 attrFlags, message);
259 break;
260
261 case BgpConstants.Update.NextHop.TYPE:
262 // Attribute Type Code NEXT_HOP
263 nextHop = parseAttributeTypeNextHop(bgpSession, ctx,
264 attrTypeCode, attrLen,
265 attrFlags, message);
266 break;
267
268 case BgpConstants.Update.MultiExitDisc.TYPE:
269 // Attribute Type Code MULTI_EXIT_DISC
270 multiExitDisc =
271 parseAttributeTypeMultiExitDisc(bgpSession, ctx,
272 attrTypeCode, attrLen,
273 attrFlags, message);
274 break;
275
276 case BgpConstants.Update.LocalPref.TYPE:
277 // Attribute Type Code LOCAL_PREF
278 localPref =
279 parseAttributeTypeLocalPref(bgpSession, ctx,
280 attrTypeCode, attrLen,
281 attrFlags, message);
282 break;
283
284 case BgpConstants.Update.AtomicAggregate.TYPE:
285 // Attribute Type Code ATOMIC_AGGREGATE
286 parseAttributeTypeAtomicAggregate(bgpSession, ctx,
287 attrTypeCode, attrLen,
288 attrFlags, message);
289 // Nothing to do: this attribute is primarily informational
290 break;
291
292 case BgpConstants.Update.Aggregator.TYPE:
293 // Attribute Type Code AGGREGATOR
294 Pair<Long, Ip4Address> aggregator =
295 parseAttributeTypeAggregator(bgpSession, ctx,
296 attrTypeCode, attrLen,
297 attrFlags, message);
298 aggregatorAsNumber = aggregator.getLeft();
299 aggregatorIpAddress = aggregator.getRight();
300 break;
301
302 default:
303 // NOTE: Parse any new Attribute Types if needed
304 if (!optionalBit) {
305 // ERROR: Unrecognized Well-known Attribute
306 actionsBgpUpdateUnrecognizedWellKnownAttribute(
307 bgpSession, ctx, attrTypeCode, attrLen, attrFlags,
308 message);
309 String errorMsg = "Unrecognized Well-known Attribute: " +
310 attrTypeCode;
311 throw new BgpParseException(errorMsg);
312 }
313
314 // Skip the data from the unrecognized attribute
315 log.debug("BGP RX UPDATE message from {}: " +
316 "Unrecognized Attribute Type {}",
317 bgpSession.getRemoteAddress(), attrTypeCode);
318 message.skipBytes(attrLen);
319 break;
320 }
321 }
322
323 // Verify the Well-known Attributes
324 verifyBgpUpdateWellKnownAttributes(bgpSession, ctx, origin, asPath,
325 nextHop, localPref);
326
327 //
328 // Parse the NLRI (Network Layer Reachability Information)
329 //
330 Collection<Ip4Prefix> addedPrefixes = null;
331 int nlriLength = message.readableBytes();
332 try {
333 addedPrefixes = parsePackedPrefixes(nlriLength, message);
334 } catch (BgpParseException e) {
335 // ERROR: Invalid Network Field
336 log.debug("Exception parsing NLRI from BGP peer {}: ",
337 bgpSession.getRemoteBgpId(), e);
338 actionsBgpUpdateInvalidNetworkField(bgpSession, ctx);
339 // Rethrow the exception
340 throw e;
341 }
342
343 // Generate the added routes
344 for (Ip4Prefix prefix : addedPrefixes) {
345 BgpRouteEntry bgpRouteEntry =
346 new BgpRouteEntry(bgpSession, prefix, nextHop,
347 origin.byteValue(), asPath, localPref);
348 bgpRouteEntry.setMultiExitDisc(multiExitDisc);
349 if (bgpRouteEntry.hasAsPathLoop(bgpSession.getLocalAs())) {
350 log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
351 "nextHop {}: contains AS Path loop",
352 bgpSession.getRemoteAddress(), prefix, nextHop);
353 continue;
354 } else {
355 log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
356 bgpSession.getRemoteAddress(), prefix, nextHop);
357 }
358 addedRoutes.put(prefix, bgpRouteEntry);
359 }
360
361 return addedRoutes.values();
362 }
363
364 /**
365 * Verifies BGP UPDATE Well-known Attributes.
366 *
367 * @param bgpSession the BGP Session to use
368 * @param ctx the Channel Handler Context
369 * @param origin the ORIGIN well-known mandatory attribute
370 * @param asPath the AS_PATH well-known mandatory attribute
371 * @param nextHop the NEXT_HOP well-known mandatory attribute
372 * @param localPref the LOCAL_PREF required attribute
373 * @throws BgpParseException
374 */
375 private static void verifyBgpUpdateWellKnownAttributes(
376 BgpSession bgpSession,
377 ChannelHandlerContext ctx,
378 Short origin,
379 BgpRouteEntry.AsPath asPath,
380 Ip4Address nextHop,
381 Long localPref)
382 throws BgpParseException {
383 //
384 // Check for Missing Well-known Attributes
385 //
386 if ((origin == null) || (origin == -1)) {
387 // Missing Attribute Type Code ORIGIN
388 int type = BgpConstants.Update.Origin.TYPE;
389 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
390 String errorMsg = "Missing Well-known Attribute: ORIGIN";
391 throw new BgpParseException(errorMsg);
392 }
393 if (asPath == null) {
394 // Missing Attribute Type Code AS_PATH
395 int type = BgpConstants.Update.AsPath.TYPE;
396 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
397 String errorMsg = "Missing Well-known Attribute: AS_PATH";
398 throw new BgpParseException(errorMsg);
399 }
400 if (nextHop == null) {
401 // Missing Attribute Type Code NEXT_HOP
402 int type = BgpConstants.Update.NextHop.TYPE;
403 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
404 String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
405 throw new BgpParseException(errorMsg);
406 }
407 if (localPref == null) {
408 // Missing Attribute Type Code LOCAL_PREF
409 // NOTE: Required for iBGP
410 int type = BgpConstants.Update.LocalPref.TYPE;
411 actionsBgpUpdateMissingWellKnownAttribute(bgpSession, ctx, type);
412 String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
413 throw new BgpParseException(errorMsg);
414 }
415 }
416
417 /**
418 * Verifies the BGP UPDATE Attribute Flags.
419 *
420 * @param bgpSession the BGP Session to use
421 * @param ctx the Channel Handler Context
422 * @param attrTypeCode the attribute type code
423 * @param attrLen the attribute length (in octets)
424 * @param attrFlags the attribute flags
425 * @param message the message to parse
426 * @throws BgpParseException
427 */
428 private static void verifyBgpUpdateAttributeFlags(
429 BgpSession bgpSession,
430 ChannelHandlerContext ctx,
431 int attrTypeCode,
432 int attrLen,
433 int attrFlags,
434 ChannelBuffer message)
435 throws BgpParseException {
436
437 //
438 // Assign the Attribute Type Name and the Well-known flag
439 //
440 String typeName = "UNKNOWN";
441 boolean isWellKnown = false;
442 switch (attrTypeCode) {
443 case BgpConstants.Update.Origin.TYPE:
444 isWellKnown = true;
445 typeName = "ORIGIN";
446 break;
447 case BgpConstants.Update.AsPath.TYPE:
448 isWellKnown = true;
449 typeName = "AS_PATH";
450 break;
451 case BgpConstants.Update.NextHop.TYPE:
452 isWellKnown = true;
453 typeName = "NEXT_HOP";
454 break;
455 case BgpConstants.Update.MultiExitDisc.TYPE:
456 isWellKnown = false;
457 typeName = "MULTI_EXIT_DISC";
458 break;
459 case BgpConstants.Update.LocalPref.TYPE:
460 isWellKnown = true;
461 typeName = "LOCAL_PREF";
462 break;
463 case BgpConstants.Update.AtomicAggregate.TYPE:
464 isWellKnown = true;
465 typeName = "ATOMIC_AGGREGATE";
466 break;
467 case BgpConstants.Update.Aggregator.TYPE:
468 isWellKnown = false;
469 typeName = "AGGREGATOR";
470 break;
471 default:
472 isWellKnown = false;
473 typeName = "UNKNOWN(" + attrTypeCode + ")";
474 break;
475 }
476
477 //
478 // Verify the Attribute Flags
479 //
480 boolean optionalBit = ((0x80 & attrFlags) != 0);
481 boolean transitiveBit = ((0x40 & attrFlags) != 0);
482 boolean partialBit = ((0x20 & attrFlags) != 0);
483 if ((isWellKnown && optionalBit) ||
484 (isWellKnown && (!transitiveBit)) ||
485 (isWellKnown && partialBit) ||
486 (optionalBit && (!transitiveBit) && partialBit)) {
487 //
488 // ERROR: The Optional bit cannot be set for Well-known attributes
489 // ERROR: The Transtive bit MUST be 1 for well-known attributes
490 // ERROR: The Partial bit MUST be 0 for well-known attributes
491 // ERROR: The Partial bit MUST be 0 for optional non-transitive
492 // attributes
493 //
494 actionsBgpUpdateAttributeFlagsError(
495 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
496 String errorMsg = "Attribute Flags Error for " + typeName + ": " +
497 attrFlags;
498 throw new BgpParseException(errorMsg);
499 }
500 }
501
502 /**
503 * Parses BGP UPDATE Attribute Type ORIGIN.
504 *
505 * @param bgpSession the BGP Session to use
506 * @param ctx the Channel Handler Context
507 * @param attrTypeCode the attribute type code
508 * @param attrLen the attribute length (in octets)
509 * @param attrFlags the attribute flags
510 * @param message the message to parse
511 * @return the parsed ORIGIN value
512 * @throws BgpParseException
513 */
514 private static short parseAttributeTypeOrigin(
515 BgpSession bgpSession,
516 ChannelHandlerContext ctx,
517 int attrTypeCode,
518 int attrLen,
519 int attrFlags,
520 ChannelBuffer message)
521 throws BgpParseException {
522
523 // Check the Attribute Length
524 if (attrLen != BgpConstants.Update.Origin.LENGTH) {
525 // ERROR: Attribute Length Error
526 actionsBgpUpdateAttributeLengthError(
527 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
528 String errorMsg = "Attribute Length Error";
529 throw new BgpParseException(errorMsg);
530 }
531
532 message.markReaderIndex();
533 short origin = message.readUnsignedByte();
534 switch (origin) {
535 case BgpConstants.Update.Origin.IGP:
536 // FALLTHROUGH
537 case BgpConstants.Update.Origin.EGP:
538 // FALLTHROUGH
539 case BgpConstants.Update.Origin.INCOMPLETE:
540 break;
541 default:
542 // ERROR: Invalid ORIGIN Attribute
543 message.resetReaderIndex();
544 actionsBgpUpdateInvalidOriginAttribute(
545 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
546 origin);
547 String errorMsg = "Invalid ORIGIN Attribute: " + origin;
548 throw new BgpParseException(errorMsg);
549 }
550
551 return origin;
552 }
553
554 /**
555 * Parses BGP UPDATE Attribute AS Path.
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
563 * @return the parsed AS Path
564 * @throws BgpParseException
565 */
566 private static BgpRouteEntry.AsPath parseAttributeTypeAsPath(
567 BgpSession bgpSession,
568 ChannelHandlerContext ctx,
569 int attrTypeCode,
570 int attrLen,
571 int attrFlags,
572 ChannelBuffer message)
573 throws BgpParseException {
574 ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
575
576 //
577 // Parse the message
578 //
579 while (attrLen > 0) {
580 if (attrLen < 2) {
581 // ERROR: Malformed AS_PATH
582 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
583 String errorMsg = "Malformed AS Path";
584 throw new BgpParseException(errorMsg);
585 }
586 // Get the Path Segment Type and Length (in number of ASes)
587 short pathSegmentType = message.readUnsignedByte();
588 short pathSegmentLength = message.readUnsignedByte();
589 attrLen -= 2;
590
591 // Verify the Path Segment Type
592 switch (pathSegmentType) {
593 case BgpConstants.Update.AsPath.AS_SET:
594 // FALLTHROUGH
595 case BgpConstants.Update.AsPath.AS_SEQUENCE:
596 // FALLTHROUGH
597 case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE:
598 // FALLTHROUGH
599 case BgpConstants.Update.AsPath.AS_CONFED_SET:
600 break;
601 default:
602 // ERROR: Invalid Path Segment Type
603 //
604 // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
605 // for "Invalid Path Segment Type", hence we return
606 // the error as "Malformed AS_PATH".
607 //
608 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
609 String errorMsg =
610 "Invalid AS Path Segment Type: " + pathSegmentType;
611 throw new BgpParseException(errorMsg);
612 }
613
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900614 // 4-octet AS number handling.
615 int asPathLen;
616 if (bgpSession.isAs4OctetCapable()) {
617 asPathLen = AsPath.AS_4OCTET_LENGTH;
618 } else {
619 asPathLen = AsPath.AS_LENGTH;
620 }
621
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800622 // Parse the AS numbers
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900623 if (asPathLen * pathSegmentLength > attrLen) {
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800624 // ERROR: Malformed AS_PATH
625 actionsBgpUpdateMalformedAsPath(bgpSession, ctx);
626 String errorMsg = "Malformed AS Path";
627 throw new BgpParseException(errorMsg);
628 }
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900629 attrLen -= (asPathLen * pathSegmentLength);
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800630 ArrayList<Long> segmentAsNumbers = new ArrayList<>();
631 while (pathSegmentLength-- > 0) {
Kunihiro Ishiguro923d9d82014-12-21 15:47:28 +0900632 long asNumber;
633 if (asPathLen == AsPath.AS_4OCTET_LENGTH) {
634 asNumber = message.readUnsignedInt();
635 } else {
636 asNumber = message.readUnsignedShort();
637 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -0800638 segmentAsNumbers.add(asNumber);
639 }
640
641 BgpRouteEntry.PathSegment pathSegment =
642 new BgpRouteEntry.PathSegment((byte) pathSegmentType,
643 segmentAsNumbers);
644 pathSegments.add(pathSegment);
645 }
646
647 return new BgpRouteEntry.AsPath(pathSegments);
648 }
649
650 /**
651 * Parses BGP UPDATE Attribute Type NEXT_HOP.
652 *
653 * @param bgpSession the BGP Session to use
654 * @param ctx the Channel Handler Context
655 * @param attrTypeCode the attribute type code
656 * @param attrLen the attribute length (in octets)
657 * @param attrFlags the attribute flags
658 * @param message the message to parse
659 * @return the parsed NEXT_HOP value
660 * @throws BgpParseException
661 */
662 private static Ip4Address parseAttributeTypeNextHop(
663 BgpSession bgpSession,
664 ChannelHandlerContext ctx,
665 int attrTypeCode,
666 int attrLen,
667 int attrFlags,
668 ChannelBuffer message)
669 throws BgpParseException {
670
671 // Check the Attribute Length
672 if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
673 // ERROR: Attribute Length Error
674 actionsBgpUpdateAttributeLengthError(
675 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
676 String errorMsg = "Attribute Length Error";
677 throw new BgpParseException(errorMsg);
678 }
679
680 message.markReaderIndex();
681 Ip4Address nextHopAddress =
682 Ip4Address.valueOf((int) message.readUnsignedInt());
683 //
684 // Check whether the NEXT_HOP IP address is semantically correct.
685 // As per RFC 4271, Section 6.3:
686 //
687 // a) It MUST NOT be the IP address of the receiving speaker
688 // b) In the case of an EBGP ....
689 //
690 // Here we check only (a), because (b) doesn't apply for us: all our
691 // peers are iBGP.
692 //
693 if (nextHopAddress.equals(bgpSession.getLocalIp4Address())) {
694 // ERROR: Invalid NEXT_HOP Attribute
695 message.resetReaderIndex();
696 actionsBgpUpdateInvalidNextHopAttribute(
697 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message,
698 nextHopAddress);
699 String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
700 throw new BgpParseException(errorMsg);
701 }
702
703 return nextHopAddress;
704 }
705
706 /**
707 * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
708 *
709 * @param bgpSession the BGP Session to use
710 * @param ctx the Channel Handler Context
711 * @param attrTypeCode the attribute type code
712 * @param attrLen the attribute length (in octets)
713 * @param attrFlags the attribute flags
714 * @param message the message to parse
715 * @return the parsed MULTI_EXIT_DISC value
716 * @throws BgpParseException
717 */
718 private static long parseAttributeTypeMultiExitDisc(
719 BgpSession bgpSession,
720 ChannelHandlerContext ctx,
721 int attrTypeCode,
722 int attrLen,
723 int attrFlags,
724 ChannelBuffer message)
725 throws BgpParseException {
726
727 // Check the Attribute Length
728 if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
729 // ERROR: Attribute Length Error
730 actionsBgpUpdateAttributeLengthError(
731 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
732 String errorMsg = "Attribute Length Error";
733 throw new BgpParseException(errorMsg);
734 }
735
736 long multiExitDisc = message.readUnsignedInt();
737 return multiExitDisc;
738 }
739
740 /**
741 * Parses BGP UPDATE Attribute Type LOCAL_PREF.
742 *
743 * @param bgpSession the BGP Session to use
744 * @param ctx the Channel Handler Context
745 * @param attrTypeCode the attribute type code
746 * @param attrLen the attribute length (in octets)
747 * @param attrFlags the attribute flags
748 * @param message the message to parse
749 * @return the parsed LOCAL_PREF value
750 * @throws BgpParseException
751 */
752 private static long parseAttributeTypeLocalPref(
753 BgpSession bgpSession,
754 ChannelHandlerContext ctx,
755 int attrTypeCode,
756 int attrLen,
757 int attrFlags,
758 ChannelBuffer message)
759 throws BgpParseException {
760
761 // Check the Attribute Length
762 if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
763 // ERROR: Attribute Length Error
764 actionsBgpUpdateAttributeLengthError(
765 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
766 String errorMsg = "Attribute Length Error";
767 throw new BgpParseException(errorMsg);
768 }
769
770 long localPref = message.readUnsignedInt();
771 return localPref;
772 }
773
774 /**
775 * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
776 *
777 * @param bgpSession the BGP Session to use
778 * @param ctx the Channel Handler Context
779 * @param attrTypeCode the attribute type code
780 * @param attrLen the attribute length (in octets)
781 * @param attrFlags the attribute flags
782 * @param message the message to parse
783 * @throws BgpParseException
784 */
785 private static void parseAttributeTypeAtomicAggregate(
786 BgpSession bgpSession,
787 ChannelHandlerContext ctx,
788 int attrTypeCode,
789 int attrLen,
790 int attrFlags,
791 ChannelBuffer message)
792 throws BgpParseException {
793
794 // Check the Attribute Length
795 if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
796 // ERROR: Attribute Length Error
797 actionsBgpUpdateAttributeLengthError(
798 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
799 String errorMsg = "Attribute Length Error";
800 throw new BgpParseException(errorMsg);
801 }
802
803 // Nothing to do: this attribute is primarily informational
804 }
805
806 /**
807 * Parses BGP UPDATE Attribute Type AGGREGATOR.
808 *
809 * @param bgpSession the BGP Session to use
810 * @param ctx the Channel Handler Context
811 * @param attrTypeCode the attribute type code
812 * @param attrLen the attribute length (in octets)
813 * @param attrFlags the attribute flags
814 * @param message the message to parse
815 * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
816 * @throws BgpParseException
817 */
818 private static Pair<Long, Ip4Address> parseAttributeTypeAggregator(
819 BgpSession bgpSession,
820 ChannelHandlerContext ctx,
821 int attrTypeCode,
822 int attrLen,
823 int attrFlags,
824 ChannelBuffer message)
825 throws BgpParseException {
826
827 // Check the Attribute Length
828 if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
829 // ERROR: Attribute Length Error
830 actionsBgpUpdateAttributeLengthError(
831 bgpSession, ctx, attrTypeCode, attrLen, attrFlags, message);
832 String errorMsg = "Attribute Length Error";
833 throw new BgpParseException(errorMsg);
834 }
835
836 // The AGGREGATOR AS number
837 long aggregatorAsNumber = message.readUnsignedShort();
838 // The AGGREGATOR IP address
839 Ip4Address aggregatorIpAddress =
840 Ip4Address.valueOf((int) message.readUnsignedInt());
841
842 Pair<Long, Ip4Address> aggregator = Pair.of(aggregatorAsNumber,
843 aggregatorIpAddress);
844 return aggregator;
845 }
846
847 /**
848 * Parses a message that contains encoded IPv4 network prefixes.
849 * <p>
850 * The IPv4 prefixes are encoded in the form:
851 * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
852 * and Prefix is the IPv4 prefix (padded with trailing bits to the end
853 * of an octet).
854 *
855 * @param totalLength the total length of the data to parse
856 * @param message the message with data to parse
857 * @return a collection of parsed IPv4 network prefixes
858 * @throws BgpParseException
859 */
860 private static Collection<Ip4Prefix> parsePackedPrefixes(
861 int totalLength,
862 ChannelBuffer message)
863 throws BgpParseException {
864 Collection<Ip4Prefix> result = new ArrayList<>();
865
866 if (totalLength == 0) {
867 return result;
868 }
869
870 // Parse the data
871 int dataEnd = message.readerIndex() + totalLength;
872 while (message.readerIndex() < dataEnd) {
873 int prefixBitlen = message.readUnsignedByte();
874 int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
875 if (message.readerIndex() + prefixBytelen > dataEnd) {
876 String errorMsg = "Malformed Network Prefixes";
877 throw new BgpParseException(errorMsg);
878 }
879
880 long address = 0;
881 long extraShift = (4 - prefixBytelen) * 8;
882 while (prefixBytelen > 0) {
883 address <<= 8;
884 address |= message.readUnsignedByte();
885 prefixBytelen--;
886 }
887 address <<= extraShift;
888 Ip4Prefix prefix =
889 Ip4Prefix.valueOf(Ip4Address.valueOf((int) address),
890 prefixBitlen);
891 result.add(prefix);
892 }
893
894 return result;
895 }
896
897 /**
898 * Applies the appropriate actions after detecting BGP UPDATE
899 * Invalid Network Field Error: send NOTIFICATION and close the channel.
900 *
901 * @param bgpSession the BGP Session to use
902 * @param ctx the Channel Handler Context
903 */
904 private static void actionsBgpUpdateInvalidNetworkField(
905 BgpSession bgpSession,
906 ChannelHandlerContext ctx) {
907 log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
908 bgpSession.getRemoteAddress());
909
910 //
911 // ERROR: Invalid Network Field
912 //
913 // Send NOTIFICATION and close the connection
914 int errorCode = UpdateMessageError.ERROR_CODE;
915 int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
916 ChannelBuffer txMessage =
917 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
918 null);
919 ctx.getChannel().write(txMessage);
920 bgpSession.closeSession(ctx);
921 }
922
923 /**
924 * Applies the appropriate actions after detecting BGP UPDATE
925 * Malformed Attribute List Error: send NOTIFICATION and close the channel.
926 *
927 * @param bgpSession the BGP Session to use
928 * @param ctx the Channel Handler Context
929 */
930 private static void actionsBgpUpdateMalformedAttributeList(
931 BgpSession bgpSession,
932 ChannelHandlerContext ctx) {
933 log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
934 bgpSession.getRemoteAddress());
935
936 //
937 // ERROR: Malformed Attribute List
938 //
939 // Send NOTIFICATION and close the connection
940 int errorCode = UpdateMessageError.ERROR_CODE;
941 int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
942 ChannelBuffer txMessage =
943 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
944 null);
945 ctx.getChannel().write(txMessage);
946 bgpSession.closeSession(ctx);
947 }
948
949 /**
950 * Applies the appropriate actions after detecting BGP UPDATE
951 * Missing Well-known Attribute Error: send NOTIFICATION and close the
952 * channel.
953 *
954 * @param bgpSession the BGP Session to use
955 * @param ctx the Channel Handler Context
956 * @param missingAttrTypeCode the missing attribute type code
957 */
958 private static void actionsBgpUpdateMissingWellKnownAttribute(
959 BgpSession bgpSession,
960 ChannelHandlerContext ctx,
961 int missingAttrTypeCode) {
962 log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
963 bgpSession.getRemoteAddress(), missingAttrTypeCode);
964
965 //
966 // ERROR: Missing Well-known Attribute
967 //
968 // Send NOTIFICATION and close the connection
969 int errorCode = UpdateMessageError.ERROR_CODE;
970 int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
971 ChannelBuffer data = ChannelBuffers.buffer(1);
972 data.writeByte(missingAttrTypeCode);
973 ChannelBuffer txMessage =
974 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
975 data);
976 ctx.getChannel().write(txMessage);
977 bgpSession.closeSession(ctx);
978 }
979
980 /**
981 * Applies the appropriate actions after detecting BGP UPDATE
982 * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
983 *
984 * @param bgpSession the BGP Session to use
985 * @param ctx the Channel Handler Context
986 * @param attrTypeCode the attribute type code
987 * @param attrLen the attribute length (in octets)
988 * @param attrFlags the attribute flags
989 * @param message the message with the data
990 * @param origin the ORIGIN attribute value
991 */
992 private static void actionsBgpUpdateInvalidOriginAttribute(
993 BgpSession bgpSession,
994 ChannelHandlerContext ctx,
995 int attrTypeCode,
996 int attrLen,
997 int attrFlags,
998 ChannelBuffer message,
999 short origin) {
1000 log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
1001 bgpSession.getRemoteAddress());
1002
1003 //
1004 // ERROR: Invalid ORIGIN Attribute
1005 //
1006 // Send NOTIFICATION and close the connection
1007 int errorCode = UpdateMessageError.ERROR_CODE;
1008 int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
1009 ChannelBuffer data =
1010 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1011 attrFlags, message);
1012 ChannelBuffer txMessage =
1013 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1014 data);
1015 ctx.getChannel().write(txMessage);
1016 bgpSession.closeSession(ctx);
1017 }
1018
1019 /**
1020 * Applies the appropriate actions after detecting BGP UPDATE
1021 * Attribute Flags Error: send NOTIFICATION and close the channel.
1022 *
1023 * @param bgpSession the BGP Session to use
1024 * @param ctx the Channel Handler Context
1025 * @param attrTypeCode the attribute type code
1026 * @param attrLen the attribute length (in octets)
1027 * @param attrFlags the attribute flags
1028 * @param message the message with the data
1029 */
1030 private static void actionsBgpUpdateAttributeFlagsError(
1031 BgpSession bgpSession,
1032 ChannelHandlerContext ctx,
1033 int attrTypeCode,
1034 int attrLen,
1035 int attrFlags,
1036 ChannelBuffer message) {
1037 log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
1038 bgpSession.getRemoteAddress());
1039
1040 //
1041 // ERROR: Attribute Flags Error
1042 //
1043 // Send NOTIFICATION and close the connection
1044 int errorCode = UpdateMessageError.ERROR_CODE;
1045 int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
1046 ChannelBuffer data =
1047 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1048 attrFlags, message);
1049 ChannelBuffer txMessage =
1050 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1051 data);
1052 ctx.getChannel().write(txMessage);
1053 bgpSession.closeSession(ctx);
1054 }
1055
1056 /**
1057 * Applies the appropriate actions after detecting BGP UPDATE
1058 * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
1059 * channel.
1060 *
1061 * @param bgpSession the BGP Session to use
1062 * @param ctx the Channel Handler Context
1063 * @param attrTypeCode the attribute type code
1064 * @param attrLen the attribute length (in octets)
1065 * @param attrFlags the attribute flags
1066 * @param message the message with the data
1067 * @param nextHop the NEXT_HOP attribute value
1068 */
1069 private static void actionsBgpUpdateInvalidNextHopAttribute(
1070 BgpSession bgpSession,
1071 ChannelHandlerContext ctx,
1072 int attrTypeCode,
1073 int attrLen,
1074 int attrFlags,
1075 ChannelBuffer message,
1076 Ip4Address nextHop) {
1077 log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
1078 bgpSession.getRemoteAddress(), nextHop);
1079
1080 //
1081 // ERROR: Invalid ORIGIN Attribute
1082 //
1083 // Send NOTIFICATION and close the connection
1084 int errorCode = UpdateMessageError.ERROR_CODE;
1085 int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
1086 ChannelBuffer data =
1087 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1088 attrFlags, message);
1089 ChannelBuffer txMessage =
1090 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1091 data);
1092 ctx.getChannel().write(txMessage);
1093 bgpSession.closeSession(ctx);
1094 }
1095
1096 /**
1097 * Applies the appropriate actions after detecting BGP UPDATE
1098 * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
1099 * the channel.
1100 *
1101 * @param bgpSession the BGP Session to use
1102 * @param ctx the Channel Handler Context
1103 * @param attrTypeCode the attribute type code
1104 * @param attrLen the attribute length (in octets)
1105 * @param attrFlags the attribute flags
1106 * @param message the message with the data
1107 */
1108 private static void actionsBgpUpdateUnrecognizedWellKnownAttribute(
1109 BgpSession bgpSession,
1110 ChannelHandlerContext ctx,
1111 int attrTypeCode,
1112 int attrLen,
1113 int attrFlags,
1114 ChannelBuffer message) {
1115 log.debug("BGP RX UPDATE Error from {}: " +
1116 "Unrecognized Well-known Attribute Error: {}",
1117 bgpSession.getRemoteAddress(), attrTypeCode);
1118
1119 //
1120 // ERROR: Unrecognized Well-known Attribute
1121 //
1122 // Send NOTIFICATION and close the connection
1123 int errorCode = UpdateMessageError.ERROR_CODE;
1124 int errorSubcode =
1125 UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
1126 ChannelBuffer data =
1127 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1128 attrFlags, message);
1129 ChannelBuffer txMessage =
1130 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1131 data);
1132 ctx.getChannel().write(txMessage);
1133 bgpSession.closeSession(ctx);
1134 }
1135
1136 /**
1137 * Applies the appropriate actions after detecting BGP UPDATE
1138 * Attribute Length Error: send NOTIFICATION and close the channel.
1139 *
1140 * @param bgpSession the BGP Session to use
1141 * @param ctx the Channel Handler Context
1142 * @param attrTypeCode the attribute type code
1143 * @param attrLen the attribute length (in octets)
1144 * @param attrFlags the attribute flags
1145 * @param message the message with the data
1146 */
1147 private static void actionsBgpUpdateAttributeLengthError(
1148 BgpSession bgpSession,
1149 ChannelHandlerContext ctx,
1150 int attrTypeCode,
1151 int attrLen,
1152 int attrFlags,
1153 ChannelBuffer message) {
1154 log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
1155 bgpSession.getRemoteAddress());
1156
1157 //
1158 // ERROR: Attribute Length Error
1159 //
1160 // Send NOTIFICATION and close the connection
1161 int errorCode = UpdateMessageError.ERROR_CODE;
1162 int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
1163 ChannelBuffer data =
1164 prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1165 attrFlags, message);
1166 ChannelBuffer txMessage =
1167 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1168 data);
1169 ctx.getChannel().write(txMessage);
1170 bgpSession.closeSession(ctx);
1171 }
1172
1173 /**
1174 * Applies the appropriate actions after detecting BGP UPDATE
1175 * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
1176 *
1177 * @param bgpSession the BGP Session to use
1178 * @param ctx the Channel Handler Context
1179 */
1180 private static void actionsBgpUpdateMalformedAsPath(
1181 BgpSession bgpSession,
1182 ChannelHandlerContext ctx) {
1183 log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
1184 bgpSession.getRemoteAddress());
1185
1186 //
1187 // ERROR: Malformed AS_PATH
1188 //
1189 // Send NOTIFICATION and close the connection
1190 int errorCode = UpdateMessageError.ERROR_CODE;
1191 int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
1192 ChannelBuffer txMessage =
1193 BgpNotification.prepareBgpNotification(errorCode, errorSubcode,
1194 null);
1195 ctx.getChannel().write(txMessage);
1196 bgpSession.closeSession(ctx);
1197 }
1198
1199 /**
1200 * Prepares BGP UPDATE Notification data payload.
1201 *
1202 * @param attrTypeCode the attribute type code
1203 * @param attrLen the attribute length (in octets)
1204 * @param attrFlags the attribute flags
1205 * @param message the message with the data
1206 * @return the buffer with the data payload for the BGP UPDATE Notification
1207 */
1208 private static ChannelBuffer prepareBgpUpdateNotificationDataPayload(
1209 int attrTypeCode,
1210 int attrLen,
1211 int attrFlags,
1212 ChannelBuffer message) {
1213 // Compute the attribute length field octets
1214 boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
1215 int attrLenOctets = 1;
1216 if (extendedLengthBit) {
1217 attrLenOctets = 2;
1218 }
1219 ChannelBuffer data =
1220 ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
1221 data.writeByte(attrTypeCode);
1222 if (extendedLengthBit) {
1223 data.writeShort(attrLen);
1224 } else {
1225 data.writeByte(attrLen);
1226 }
1227 data.writeBytes(message, attrLen);
1228 return data;
1229 }
Pavlin Radoslavov80f3e182014-12-15 10:46:18 -08001230}