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