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