Refactor the BGP code in the SDN-IP application:
* Resolves ONOS-476 * Moved the BGP message-specific processing from class BgpSession to per-message type classes: BgpKeepalive, BgpNotification, BgpOpen, BgpUpdate * Minor modifications in some of the methods or BGP-specific API to accomodate the above change. No functional changes. Change-Id: I95df128fa31c60397a279aaca25a487b7991a6e1
Showing
9 changed files
with
1850 additions
and
1495 deletions
| ... | @@ -90,8 +90,9 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -90,8 +90,9 @@ class BgpFrameDecoder extends FrameDecoder { |
| 90 | int errorSubcode = | 90 | int errorSubcode = |
| 91 | MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED; | 91 | MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED; |
| 92 | ChannelBuffer txMessage = | 92 | ChannelBuffer txMessage = |
| 93 | - bgpSession.prepareBgpNotification(errorCode, errorSubcode, | 93 | + BgpNotification.prepareBgpNotification(errorCode, |
| 94 | - null); | 94 | + errorSubcode, |
| 95 | + null); | ||
| 95 | ctx.getChannel().write(txMessage); | 96 | ctx.getChannel().write(txMessage); |
| 96 | bgpSession.closeSession(ctx); | 97 | bgpSession.closeSession(ctx); |
| 97 | return null; | 98 | return null; |
| ... | @@ -114,7 +115,7 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -114,7 +115,7 @@ class BgpFrameDecoder extends FrameDecoder { |
| 114 | // | 115 | // |
| 115 | // Send NOTIFICATION and close the connection | 116 | // Send NOTIFICATION and close the connection |
| 116 | ChannelBuffer txMessage = | 117 | ChannelBuffer txMessage = |
| 117 | - bgpSession.prepareBgpNotificationBadMessageLength(length); | 118 | + BgpNotification.prepareBgpNotificationBadMessageLength(length); |
| 118 | ctx.getChannel().write(txMessage); | 119 | ctx.getChannel().write(txMessage); |
| 119 | bgpSession.closeSession(ctx); | 120 | bgpSession.closeSession(ctx); |
| 120 | return null; | 121 | return null; |
| ... | @@ -145,16 +146,16 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -145,16 +146,16 @@ class BgpFrameDecoder extends FrameDecoder { |
| 145 | // | 146 | // |
| 146 | switch (type) { | 147 | switch (type) { |
| 147 | case BgpConstants.BGP_TYPE_OPEN: | 148 | case BgpConstants.BGP_TYPE_OPEN: |
| 148 | - bgpSession.processBgpOpen(ctx, message); | 149 | + BgpOpen.processBgpOpen(bgpSession, ctx, message); |
| 149 | break; | 150 | break; |
| 150 | case BgpConstants.BGP_TYPE_UPDATE: | 151 | case BgpConstants.BGP_TYPE_UPDATE: |
| 151 | - bgpSession.processBgpUpdate(ctx, message); | 152 | + BgpUpdate.processBgpUpdate(bgpSession, ctx, message); |
| 152 | break; | 153 | break; |
| 153 | case BgpConstants.BGP_TYPE_NOTIFICATION: | 154 | case BgpConstants.BGP_TYPE_NOTIFICATION: |
| 154 | - bgpSession.processBgpNotification(ctx, message); | 155 | + BgpNotification.processBgpNotification(bgpSession, ctx, message); |
| 155 | break; | 156 | break; |
| 156 | case BgpConstants.BGP_TYPE_KEEPALIVE: | 157 | case BgpConstants.BGP_TYPE_KEEPALIVE: |
| 157 | - bgpSession.processBgpKeepalive(ctx, message); | 158 | + BgpKeepalive.processBgpKeepalive(bgpSession, ctx, message); |
| 158 | break; | 159 | break; |
| 159 | default: | 160 | default: |
| 160 | // | 161 | // |
| ... | @@ -166,8 +167,8 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -166,8 +167,8 @@ class BgpFrameDecoder extends FrameDecoder { |
| 166 | ChannelBuffer data = ChannelBuffers.buffer(1); | 167 | ChannelBuffer data = ChannelBuffers.buffer(1); |
| 167 | data.writeByte(type); | 168 | data.writeByte(type); |
| 168 | ChannelBuffer txMessage = | 169 | ChannelBuffer txMessage = |
| 169 | - bgpSession.prepareBgpNotification(errorCode, errorSubcode, | 170 | + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, |
| 170 | - data); | 171 | + data); |
| 171 | ctx.getChannel().write(txMessage); | 172 | ctx.getChannel().write(txMessage); |
| 172 | bgpSession.closeSession(ctx); | 173 | bgpSession.closeSession(ctx); |
| 173 | return null; | 174 | return null; | ... | ... |
| 1 | +/* | ||
| 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 | + */ | ||
| 16 | +package org.onosproject.sdnip.bgp; | ||
| 17 | + | ||
| 18 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
| 19 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
| 20 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
| 21 | +import org.slf4j.Logger; | ||
| 22 | +import org.slf4j.LoggerFactory; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * A class for handling BGP KEEPALIVE messages. | ||
| 26 | + */ | ||
| 27 | +final class BgpKeepalive { | ||
| 28 | + private static final Logger log = | ||
| 29 | + LoggerFactory.getLogger(BgpKeepalive.class); | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * Default constructor. | ||
| 33 | + * <p> | ||
| 34 | + * The constructor is private to prevent creating an instance of | ||
| 35 | + * this utility class. | ||
| 36 | + */ | ||
| 37 | + private BgpKeepalive() { | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * Processes BGP KEEPALIVE message. | ||
| 42 | + * | ||
| 43 | + * @param bgpSession the BGP Session to use | ||
| 44 | + * @param ctx the Channel Handler Context | ||
| 45 | + * @param message the message to process | ||
| 46 | + */ | ||
| 47 | + static void processBgpKeepalive(BgpSession bgpSession, | ||
| 48 | + ChannelHandlerContext ctx, | ||
| 49 | + ChannelBuffer message) { | ||
| 50 | + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH != | ||
| 51 | + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) { | ||
| 52 | + log.debug("BGP RX KEEPALIVE Error from {}: " + | ||
| 53 | + "Invalid total message length {}. Expected {}", | ||
| 54 | + bgpSession.getRemoteAddress(), | ||
| 55 | + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH, | ||
| 56 | + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH); | ||
| 57 | + // | ||
| 58 | + // ERROR: Bad Message Length | ||
| 59 | + // | ||
| 60 | + // Send NOTIFICATION and close the connection | ||
| 61 | + ChannelBuffer txMessage = | ||
| 62 | + BgpNotification.prepareBgpNotificationBadMessageLength( | ||
| 63 | + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); | ||
| 64 | + ctx.getChannel().write(txMessage); | ||
| 65 | + bgpSession.closeSession(ctx); | ||
| 66 | + return; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + // | ||
| 70 | + // Parse the KEEPALIVE message: nothing to do | ||
| 71 | + // | ||
| 72 | + log.trace("BGP RX KEEPALIVE message from {}", | ||
| 73 | + bgpSession.getRemoteAddress()); | ||
| 74 | + | ||
| 75 | + // Start the Session Timeout timer | ||
| 76 | + bgpSession.restartSessionTimeoutTimer(ctx); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Prepares BGP KEEPALIVE message. | ||
| 81 | + * | ||
| 82 | + * @return the message to transmit (BGP header included) | ||
| 83 | + */ | ||
| 84 | + static ChannelBuffer prepareBgpKeepalive() { | ||
| 85 | + ChannelBuffer message = | ||
| 86 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 87 | + | ||
| 88 | + // | ||
| 89 | + // Prepare the KEEPALIVE message payload: nothing to do | ||
| 90 | + // | ||
| 91 | + return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, | ||
| 92 | + message); | ||
| 93 | + } | ||
| 94 | +} |
| 1 | +/* | ||
| 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 | + */ | ||
| 16 | +package org.onosproject.sdnip.bgp; | ||
| 17 | + | ||
| 18 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
| 19 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
| 20 | +import org.slf4j.Logger; | ||
| 21 | +import org.slf4j.LoggerFactory; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * A class for preparing BGP messages. | ||
| 25 | + */ | ||
| 26 | +final class BgpMessage { | ||
| 27 | + private static final Logger log = | ||
| 28 | + LoggerFactory.getLogger(BgpMessage.class); | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * Default constructor. | ||
| 32 | + * <p> | ||
| 33 | + * The constructor is private to prevent creating an instance of | ||
| 34 | + * this utility class. | ||
| 35 | + */ | ||
| 36 | + private BgpMessage() { | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * Prepares BGP message. | ||
| 41 | + * | ||
| 42 | + * @param type the BGP message type | ||
| 43 | + * @param payload the message payload to transmit (BGP header excluded) | ||
| 44 | + * @return the message to transmit (BGP header included) | ||
| 45 | + */ | ||
| 46 | + static ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) { | ||
| 47 | + ChannelBuffer message = | ||
| 48 | + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH + | ||
| 49 | + payload.readableBytes()); | ||
| 50 | + | ||
| 51 | + // Write the marker | ||
| 52 | + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) { | ||
| 53 | + message.writeByte(0xff); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + // Write the rest of the BGP header | ||
| 57 | + message.writeShort(BgpConstants.BGP_HEADER_LENGTH + | ||
| 58 | + payload.readableBytes()); | ||
| 59 | + message.writeByte(type); | ||
| 60 | + | ||
| 61 | + // Write the payload | ||
| 62 | + message.writeBytes(payload); | ||
| 63 | + return message; | ||
| 64 | + } | ||
| 65 | +} |
| 1 | +/* | ||
| 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 | + */ | ||
| 16 | +package org.onosproject.sdnip.bgp; | ||
| 17 | + | ||
| 18 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
| 19 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
| 20 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
| 21 | +import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError; | ||
| 22 | +import org.slf4j.Logger; | ||
| 23 | +import org.slf4j.LoggerFactory; | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * A class for handling BGP NOTIFICATION messages. | ||
| 27 | + */ | ||
| 28 | +final class BgpNotification { | ||
| 29 | + private static final Logger log = | ||
| 30 | + LoggerFactory.getLogger(BgpNotification.class); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * Default constructor. | ||
| 34 | + * <p> | ||
| 35 | + * The constructor is private to prevent creating an instance of | ||
| 36 | + * this utility class. | ||
| 37 | + */ | ||
| 38 | + private BgpNotification() { | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * Processes BGP NOTIFICATION message. | ||
| 43 | + * | ||
| 44 | + * @param bgpSession the BGP Session to use | ||
| 45 | + * @param ctx the Channel Handler Context | ||
| 46 | + * @param message the message to process | ||
| 47 | + */ | ||
| 48 | + static void processBgpNotification(BgpSession bgpSession, | ||
| 49 | + ChannelHandlerContext ctx, | ||
| 50 | + ChannelBuffer message) { | ||
| 51 | + int minLength = | ||
| 52 | + BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
| 53 | + if (message.readableBytes() < minLength) { | ||
| 54 | + log.debug("BGP RX NOTIFICATION Error from {}: " + | ||
| 55 | + "Message length {} too short. Must be at least {}", | ||
| 56 | + bgpSession.getRemoteAddress(), message.readableBytes(), | ||
| 57 | + minLength); | ||
| 58 | + // | ||
| 59 | + // ERROR: Bad Message Length | ||
| 60 | + // | ||
| 61 | + // NOTE: We do NOT send NOTIFICATION in response to a notification | ||
| 62 | + return; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + // | ||
| 66 | + // Parse the NOTIFICATION message | ||
| 67 | + // | ||
| 68 | + int errorCode = message.readUnsignedByte(); | ||
| 69 | + int errorSubcode = message.readUnsignedByte(); | ||
| 70 | + int dataLength = message.readableBytes(); | ||
| 71 | + | ||
| 72 | + log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " + | ||
| 73 | + "Error Subcode {} Data Length {}", | ||
| 74 | + bgpSession.getRemoteAddress(), errorCode, errorSubcode, | ||
| 75 | + dataLength); | ||
| 76 | + | ||
| 77 | + // | ||
| 78 | + // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to | ||
| 79 | + // close the connection. | ||
| 80 | + // | ||
| 81 | + | ||
| 82 | + // Start the Session Timeout timer | ||
| 83 | + bgpSession.restartSessionTimeoutTimer(ctx); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * Prepares BGP NOTIFICATION message. | ||
| 88 | + * | ||
| 89 | + * @param errorCode the BGP NOTIFICATION Error Code | ||
| 90 | + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable, | ||
| 91 | + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC | ||
| 92 | + * @param data the BGP NOTIFICATION Data if applicable, otherwise null | ||
| 93 | + * @return the message to transmit (BGP header included) | ||
| 94 | + */ | ||
| 95 | + static ChannelBuffer prepareBgpNotification(int errorCode, | ||
| 96 | + int errorSubcode, | ||
| 97 | + ChannelBuffer data) { | ||
| 98 | + ChannelBuffer message = | ||
| 99 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 100 | + | ||
| 101 | + // | ||
| 102 | + // Prepare the NOTIFICATION message payload | ||
| 103 | + // | ||
| 104 | + message.writeByte(errorCode); | ||
| 105 | + message.writeByte(errorSubcode); | ||
| 106 | + if (data != null) { | ||
| 107 | + message.writeBytes(data); | ||
| 108 | + } | ||
| 109 | + return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, | ||
| 110 | + message); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * Prepares BGP NOTIFICATION message: Bad Message Length. | ||
| 115 | + * | ||
| 116 | + * @param length the erroneous Length field | ||
| 117 | + * @return the message to transmit (BGP header included) | ||
| 118 | + */ | ||
| 119 | + static ChannelBuffer prepareBgpNotificationBadMessageLength(int length) { | ||
| 120 | + int errorCode = MessageHeaderError.ERROR_CODE; | ||
| 121 | + int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH; | ||
| 122 | + ChannelBuffer data = ChannelBuffers.buffer(2); | ||
| 123 | + data.writeShort(length); | ||
| 124 | + | ||
| 125 | + return prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 126 | + } | ||
| 127 | +} |
| 1 | +/* | ||
| 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 | + */ | ||
| 16 | +package org.onosproject.sdnip.bgp; | ||
| 17 | + | ||
| 18 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
| 19 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
| 20 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
| 21 | +import org.onlab.packet.Ip4Address; | ||
| 22 | +import org.onosproject.sdnip.bgp.BgpConstants.Notifications; | ||
| 23 | +import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError; | ||
| 24 | +import org.slf4j.Logger; | ||
| 25 | +import org.slf4j.LoggerFactory; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * A class for handling BGP OPEN messages. | ||
| 29 | + */ | ||
| 30 | +final class BgpOpen { | ||
| 31 | + private static final Logger log = LoggerFactory.getLogger(BgpOpen.class); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * Default constructor. | ||
| 35 | + * <p> | ||
| 36 | + * The constructor is private to prevent creating an instance of | ||
| 37 | + * this utility class. | ||
| 38 | + */ | ||
| 39 | + private BgpOpen() { | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * Processes BGP OPEN message. | ||
| 44 | + * | ||
| 45 | + * @param bgpSession the BGP Session to use | ||
| 46 | + * @param ctx the Channel Handler Context | ||
| 47 | + * @param message the message to process | ||
| 48 | + */ | ||
| 49 | + static void processBgpOpen(BgpSession bgpSession, | ||
| 50 | + ChannelHandlerContext ctx, | ||
| 51 | + ChannelBuffer message) { | ||
| 52 | + int minLength = | ||
| 53 | + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
| 54 | + if (message.readableBytes() < minLength) { | ||
| 55 | + log.debug("BGP RX OPEN Error from {}: " + | ||
| 56 | + "Message length {} too short. Must be at least {}", | ||
| 57 | + bgpSession.getRemoteAddress(), message.readableBytes(), | ||
| 58 | + minLength); | ||
| 59 | + // | ||
| 60 | + // ERROR: Bad Message Length | ||
| 61 | + // | ||
| 62 | + // Send NOTIFICATION and close the connection | ||
| 63 | + ChannelBuffer txMessage = | ||
| 64 | + BgpNotification.prepareBgpNotificationBadMessageLength( | ||
| 65 | + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); | ||
| 66 | + ctx.getChannel().write(txMessage); | ||
| 67 | + bgpSession.closeSession(ctx); | ||
| 68 | + return; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + // | ||
| 72 | + // Parse the OPEN message | ||
| 73 | + // | ||
| 74 | + // Remote BGP version | ||
| 75 | + int remoteBgpVersion = message.readUnsignedByte(); | ||
| 76 | + if (remoteBgpVersion != BgpConstants.BGP_VERSION) { | ||
| 77 | + log.debug("BGP RX OPEN Error from {}: " + | ||
| 78 | + "Unsupported BGP version {}. Should be {}", | ||
| 79 | + bgpSession.getRemoteAddress(), remoteBgpVersion, | ||
| 80 | + BgpConstants.BGP_VERSION); | ||
| 81 | + // | ||
| 82 | + // ERROR: Unsupported Version Number | ||
| 83 | + // | ||
| 84 | + // Send NOTIFICATION and close the connection | ||
| 85 | + int errorCode = OpenMessageError.ERROR_CODE; | ||
| 86 | + int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER; | ||
| 87 | + ChannelBuffer data = ChannelBuffers.buffer(2); | ||
| 88 | + data.writeShort(BgpConstants.BGP_VERSION); | ||
| 89 | + ChannelBuffer txMessage = | ||
| 90 | + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, | ||
| 91 | + data); | ||
| 92 | + ctx.getChannel().write(txMessage); | ||
| 93 | + bgpSession.closeSession(ctx); | ||
| 94 | + return; | ||
| 95 | + } | ||
| 96 | + bgpSession.setRemoteBgpVersion(remoteBgpVersion); | ||
| 97 | + | ||
| 98 | + // Remote AS number | ||
| 99 | + long remoteAs = message.readUnsignedShort(); | ||
| 100 | + // | ||
| 101 | + // Verify that the AS number is same for all other BGP Sessions | ||
| 102 | + // NOTE: This check applies only for our use-case where all BGP | ||
| 103 | + // sessions are iBGP. | ||
| 104 | + // | ||
| 105 | + for (BgpSession bs : bgpSession.getBgpSessionManager().getBgpSessions()) { | ||
| 106 | + if ((bs.getRemoteAs() != 0) && (remoteAs != bs.getRemoteAs())) { | ||
| 107 | + log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " + | ||
| 108 | + "Expected {}", | ||
| 109 | + bgpSession.getRemoteAddress(), remoteAs, | ||
| 110 | + bs.getRemoteAs()); | ||
| 111 | + // | ||
| 112 | + // ERROR: Bad Peer AS | ||
| 113 | + // | ||
| 114 | + // Send NOTIFICATION and close the connection | ||
| 115 | + int errorCode = OpenMessageError.ERROR_CODE; | ||
| 116 | + int errorSubcode = OpenMessageError.BAD_PEER_AS; | ||
| 117 | + ChannelBuffer txMessage = | ||
| 118 | + BgpNotification.prepareBgpNotification(errorCode, | ||
| 119 | + errorSubcode, null); | ||
| 120 | + ctx.getChannel().write(txMessage); | ||
| 121 | + bgpSession.closeSession(ctx); | ||
| 122 | + return; | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + bgpSession.setRemoteAs(remoteAs); | ||
| 126 | + | ||
| 127 | + // Remote Hold Time | ||
| 128 | + long remoteHoldtime = message.readUnsignedShort(); | ||
| 129 | + if ((remoteHoldtime != 0) && | ||
| 130 | + (remoteHoldtime < BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME)) { | ||
| 131 | + log.debug("BGP RX OPEN Error from {}: " + | ||
| 132 | + "Unacceptable Hold Time field {}. " + | ||
| 133 | + "Should be 0 or at least {}", | ||
| 134 | + bgpSession.getRemoteAddress(), remoteHoldtime, | ||
| 135 | + BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME); | ||
| 136 | + // | ||
| 137 | + // ERROR: Unacceptable Hold Time | ||
| 138 | + // | ||
| 139 | + // Send NOTIFICATION and close the connection | ||
| 140 | + int errorCode = OpenMessageError.ERROR_CODE; | ||
| 141 | + int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME; | ||
| 142 | + ChannelBuffer txMessage = | ||
| 143 | + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, | ||
| 144 | + null); | ||
| 145 | + ctx.getChannel().write(txMessage); | ||
| 146 | + bgpSession.closeSession(ctx); | ||
| 147 | + return; | ||
| 148 | + } | ||
| 149 | + bgpSession.setRemoteHoldtime(remoteHoldtime); | ||
| 150 | + | ||
| 151 | + // Remote BGP Identifier | ||
| 152 | + Ip4Address remoteBgpId = | ||
| 153 | + Ip4Address.valueOf((int) message.readUnsignedInt()); | ||
| 154 | + bgpSession.setRemoteBgpId(remoteBgpId); | ||
| 155 | + | ||
| 156 | + // Optional Parameters | ||
| 157 | + int optParamLen = message.readUnsignedByte(); | ||
| 158 | + if (message.readableBytes() < optParamLen) { | ||
| 159 | + log.debug("BGP RX OPEN Error from {}: " + | ||
| 160 | + "Invalid Optional Parameter Length field {}. " + | ||
| 161 | + "Remaining Optional Parameters {}", | ||
| 162 | + bgpSession.getRemoteAddress(), optParamLen, | ||
| 163 | + message.readableBytes()); | ||
| 164 | + // | ||
| 165 | + // ERROR: Invalid Optional Parameter Length field: Unspecific | ||
| 166 | + // | ||
| 167 | + // Send NOTIFICATION and close the connection | ||
| 168 | + int errorCode = OpenMessageError.ERROR_CODE; | ||
| 169 | + int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC; | ||
| 170 | + ChannelBuffer txMessage = | ||
| 171 | + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, | ||
| 172 | + null); | ||
| 173 | + ctx.getChannel().write(txMessage); | ||
| 174 | + bgpSession.closeSession(ctx); | ||
| 175 | + return; | ||
| 176 | + } | ||
| 177 | + // NOTE: Parse the optional parameters (if needed) | ||
| 178 | + message.readBytes(optParamLen); // NOTE: data ignored | ||
| 179 | + | ||
| 180 | + log.debug("BGP RX OPEN message from {}: " + | ||
| 181 | + "BGPv{} AS {} BGP-ID {} Holdtime {}", | ||
| 182 | + bgpSession.getRemoteAddress(), remoteBgpVersion, remoteAs, | ||
| 183 | + remoteBgpId, remoteHoldtime); | ||
| 184 | + | ||
| 185 | + // Send my OPEN followed by KEEPALIVE | ||
| 186 | + ChannelBuffer txMessage = prepareBgpOpen(bgpSession); | ||
| 187 | + ctx.getChannel().write(txMessage); | ||
| 188 | + // | ||
| 189 | + txMessage = BgpKeepalive.prepareBgpKeepalive(); | ||
| 190 | + ctx.getChannel().write(txMessage); | ||
| 191 | + | ||
| 192 | + // Start the KEEPALIVE timer | ||
| 193 | + bgpSession.restartKeepaliveTimer(ctx); | ||
| 194 | + | ||
| 195 | + // Start the Session Timeout timer | ||
| 196 | + bgpSession.restartSessionTimeoutTimer(ctx); | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + /** | ||
| 200 | + * Prepares BGP OPEN message. | ||
| 201 | + * | ||
| 202 | + * @param bgpSession the BGP Session to use | ||
| 203 | + * @return the message to transmit (BGP header included) | ||
| 204 | + */ | ||
| 205 | + private static ChannelBuffer prepareBgpOpen(BgpSession bgpSession) { | ||
| 206 | + ChannelBuffer message = | ||
| 207 | + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 208 | + | ||
| 209 | + // | ||
| 210 | + // Prepare the OPEN message payload | ||
| 211 | + // | ||
| 212 | + message.writeByte(bgpSession.getLocalBgpVersion()); | ||
| 213 | + message.writeShort((int) bgpSession.getLocalAs()); | ||
| 214 | + message.writeShort((int) bgpSession.getLocalHoldtime()); | ||
| 215 | + message.writeInt(bgpSession.getLocalBgpId().toInt()); | ||
| 216 | + message.writeByte(0); // No Optional Parameters | ||
| 217 | + return BgpMessage.prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, | ||
| 218 | + message); | ||
| 219 | + } | ||
| 220 | +} |
| ... | @@ -18,18 +18,14 @@ package org.onosproject.sdnip.bgp; | ... | @@ -18,18 +18,14 @@ package org.onosproject.sdnip.bgp; |
| 18 | import java.net.InetAddress; | 18 | import java.net.InetAddress; |
| 19 | import java.net.InetSocketAddress; | 19 | import java.net.InetSocketAddress; |
| 20 | import java.net.SocketAddress; | 20 | import java.net.SocketAddress; |
| 21 | -import java.util.ArrayList; | ||
| 22 | import java.util.Collection; | 21 | import java.util.Collection; |
| 23 | import java.util.Collections; | 22 | import java.util.Collections; |
| 24 | -import java.util.HashMap; | ||
| 25 | import java.util.Map; | 23 | import java.util.Map; |
| 26 | import java.util.concurrent.ConcurrentHashMap; | 24 | import java.util.concurrent.ConcurrentHashMap; |
| 27 | import java.util.concurrent.ConcurrentMap; | 25 | import java.util.concurrent.ConcurrentMap; |
| 28 | import java.util.concurrent.TimeUnit; | 26 | import java.util.concurrent.TimeUnit; |
| 29 | 27 | ||
| 30 | -import org.apache.commons.lang3.tuple.Pair; | ||
| 31 | import org.jboss.netty.buffer.ChannelBuffer; | 28 | import org.jboss.netty.buffer.ChannelBuffer; |
| 32 | -import org.jboss.netty.buffer.ChannelBuffers; | ||
| 33 | import org.jboss.netty.channel.ChannelHandlerContext; | 29 | import org.jboss.netty.channel.ChannelHandlerContext; |
| 34 | import org.jboss.netty.channel.ChannelStateEvent; | 30 | import org.jboss.netty.channel.ChannelStateEvent; |
| 35 | import org.jboss.netty.channel.ExceptionEvent; | 31 | import org.jboss.netty.channel.ExceptionEvent; |
| ... | @@ -42,9 +38,6 @@ import org.onlab.packet.Ip4Address; | ... | @@ -42,9 +38,6 @@ import org.onlab.packet.Ip4Address; |
| 42 | import org.onlab.packet.Ip4Prefix; | 38 | import org.onlab.packet.Ip4Prefix; |
| 43 | import org.onosproject.sdnip.bgp.BgpConstants.Notifications; | 39 | import org.onosproject.sdnip.bgp.BgpConstants.Notifications; |
| 44 | import org.onosproject.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired; | 40 | import org.onosproject.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired; |
| 45 | -import org.onosproject.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError; | ||
| 46 | -import org.onosproject.sdnip.bgp.BgpConstants.Notifications.OpenMessageError; | ||
| 47 | -import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError; | ||
| 48 | import org.slf4j.Logger; | 41 | import org.slf4j.Logger; |
| 49 | import org.slf4j.LoggerFactory; | 42 | import org.slf4j.LoggerFactory; |
| 50 | 43 | ||
| ... | @@ -94,6 +87,18 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -94,6 +87,18 @@ public class BgpSession extends SimpleChannelHandler { |
| 94 | */ | 87 | */ |
| 95 | BgpSession(BgpSessionManager bgpSessionManager) { | 88 | BgpSession(BgpSessionManager bgpSessionManager) { |
| 96 | this.bgpSessionManager = bgpSessionManager; | 89 | this.bgpSessionManager = bgpSessionManager; |
| 90 | + | ||
| 91 | + // NOTE: We support only BGP4 | ||
| 92 | + this.localBgpVersion = BgpConstants.BGP_VERSION; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + /** | ||
| 96 | + * Gets the BGP Session Manager. | ||
| 97 | + * | ||
| 98 | + * @return the BGP Session Manager | ||
| 99 | + */ | ||
| 100 | + BgpSessionManager getBgpSessionManager() { | ||
| 101 | + return bgpSessionManager; | ||
| 97 | } | 102 | } |
| 98 | 103 | ||
| 99 | /** | 104 | /** |
| ... | @@ -101,8 +106,8 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -101,8 +106,8 @@ public class BgpSession extends SimpleChannelHandler { |
| 101 | * | 106 | * |
| 102 | * @return the BGP RIB-IN routing entries | 107 | * @return the BGP RIB-IN routing entries |
| 103 | */ | 108 | */ |
| 104 | - public Collection<BgpRouteEntry> getBgpRibIn() { | 109 | + public Map<Ip4Prefix, BgpRouteEntry> bgpRibIn() { |
| 105 | - return bgpRibIn.values(); | 110 | + return bgpRibIn; |
| 106 | } | 111 | } |
| 107 | 112 | ||
| 108 | /** | 113 | /** |
| ... | @@ -143,6 +148,15 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -143,6 +148,15 @@ public class BgpSession extends SimpleChannelHandler { |
| 143 | } | 148 | } |
| 144 | 149 | ||
| 145 | /** | 150 | /** |
| 151 | + * Sets the BGP session remote BGP version. | ||
| 152 | + * | ||
| 153 | + * @param remoteBgpVersion the BGP session remote BGP version to set | ||
| 154 | + */ | ||
| 155 | + void setRemoteBgpVersion(int remoteBgpVersion) { | ||
| 156 | + this.remoteBgpVersion = remoteBgpVersion; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + /** | ||
| 146 | * Gets the BGP session remote AS number. | 160 | * Gets the BGP session remote AS number. |
| 147 | * | 161 | * |
| 148 | * @return the BGP session remote AS number | 162 | * @return the BGP session remote AS number |
| ... | @@ -152,6 +166,23 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -152,6 +166,23 @@ public class BgpSession extends SimpleChannelHandler { |
| 152 | } | 166 | } |
| 153 | 167 | ||
| 154 | /** | 168 | /** |
| 169 | + * Sets the BGP session remote AS number. | ||
| 170 | + * | ||
| 171 | + * @param remoteAs the BGP session remote AS number to set | ||
| 172 | + */ | ||
| 173 | + void setRemoteAs(long remoteAs) { | ||
| 174 | + this.remoteAs = remoteAs; | ||
| 175 | + | ||
| 176 | + // | ||
| 177 | + // NOTE: Currently, the local AS number is always set to the remote AS. | ||
| 178 | + // This is done, because the peer setup is always iBGP. | ||
| 179 | + // In the future the local AS number should be configured as part | ||
| 180 | + // of an explicit BGP peering configuration. | ||
| 181 | + // | ||
| 182 | + this.localAs = remoteAs; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + /** | ||
| 155 | * Gets the BGP session remote Holdtime. | 186 | * Gets the BGP session remote Holdtime. |
| 156 | * | 187 | * |
| 157 | * @return the BGP session remote Holdtime | 188 | * @return the BGP session remote Holdtime |
| ... | @@ -161,6 +192,32 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -161,6 +192,32 @@ public class BgpSession extends SimpleChannelHandler { |
| 161 | } | 192 | } |
| 162 | 193 | ||
| 163 | /** | 194 | /** |
| 195 | + * Sets the BGP session remote Holdtime. | ||
| 196 | + * | ||
| 197 | + * @param remoteHoldtime the BGP session remote Holdtime to set | ||
| 198 | + */ | ||
| 199 | + void setRemoteHoldtime(long remoteHoldtime) { | ||
| 200 | + this.remoteHoldtime = remoteHoldtime; | ||
| 201 | + | ||
| 202 | + // | ||
| 203 | + // NOTE: Currently. the local BGP Holdtime is always set to the remote | ||
| 204 | + // BGP holdtime. | ||
| 205 | + // In the future, the local BGP Holdtime should be configured as part | ||
| 206 | + // of an explicit BGP peering configuration. | ||
| 207 | + // | ||
| 208 | + this.localHoldtime = remoteHoldtime; | ||
| 209 | + | ||
| 210 | + // Set the local Keepalive interval | ||
| 211 | + if (localHoldtime == 0) { | ||
| 212 | + localKeepaliveInterval = 0; | ||
| 213 | + } else { | ||
| 214 | + localKeepaliveInterval = Math.max(localHoldtime / | ||
| 215 | + BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL, | ||
| 216 | + BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL); | ||
| 217 | + } | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + /** | ||
| 164 | * Gets the BGP session remote BGP Identifier as an IPv4 address. | 221 | * Gets the BGP session remote BGP Identifier as an IPv4 address. |
| 165 | * | 222 | * |
| 166 | * @return the BGP session remote BGP Identifier as an IPv4 address | 223 | * @return the BGP session remote BGP Identifier as an IPv4 address |
| ... | @@ -170,6 +227,15 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -170,6 +227,15 @@ public class BgpSession extends SimpleChannelHandler { |
| 170 | } | 227 | } |
| 171 | 228 | ||
| 172 | /** | 229 | /** |
| 230 | + * Sets the BGP session remote BGP Identifier as an IPv4 address. | ||
| 231 | + * | ||
| 232 | + * @param remoteBgpId the BGP session remote BGP Identifier to set | ||
| 233 | + */ | ||
| 234 | + void setRemoteBgpId(Ip4Address remoteBgpId) { | ||
| 235 | + this.remoteBgpId = remoteBgpId; | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + /** | ||
| 173 | * Gets the BGP session local address. | 239 | * Gets the BGP session local address. |
| 174 | * | 240 | * |
| 175 | * @return the BGP session local address | 241 | * @return the BGP session local address |
| ... | @@ -179,6 +245,15 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -179,6 +245,15 @@ public class BgpSession extends SimpleChannelHandler { |
| 179 | } | 245 | } |
| 180 | 246 | ||
| 181 | /** | 247 | /** |
| 248 | + * Gets the BGP session local IPv4 address. | ||
| 249 | + * | ||
| 250 | + * @return the BGP session local IPv4 address | ||
| 251 | + */ | ||
| 252 | + public Ip4Address getLocalIp4Address() { | ||
| 253 | + return localIp4Address; | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + /** | ||
| 182 | * Gets the BGP session local BGP version. | 257 | * Gets the BGP session local BGP version. |
| 183 | * | 258 | * |
| 184 | * @return the BGP session local BGP version | 259 | * @return the BGP session local BGP version |
| ... | @@ -282,6 +357,12 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -282,6 +357,12 @@ public class BgpSession extends SimpleChannelHandler { |
| 282 | remoteAddress); | 357 | remoteAddress); |
| 283 | ctx.getChannel().close(); | 358 | ctx.getChannel().close(); |
| 284 | } | 359 | } |
| 360 | + | ||
| 361 | + // | ||
| 362 | + // Assign the local BGP ID | ||
| 363 | + // NOTE: This should be configuration-based | ||
| 364 | + // | ||
| 365 | + localBgpId = bgpSessionManager.getMyBgpId(); | ||
| 285 | } | 366 | } |
| 286 | 367 | ||
| 287 | @Override | 368 | @Override |
| ... | @@ -326,1458 +407,9 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -326,1458 +407,9 @@ public class BgpSession extends SimpleChannelHandler { |
| 326 | } | 407 | } |
| 327 | 408 | ||
| 328 | /** | 409 | /** |
| 329 | - * Processes BGP OPEN message. | ||
| 330 | - * | ||
| 331 | - * @param ctx the Channel Handler Context | ||
| 332 | - * @param message the message to process | ||
| 333 | - */ | ||
| 334 | - void processBgpOpen(ChannelHandlerContext ctx, ChannelBuffer message) { | ||
| 335 | - int minLength = | ||
| 336 | - BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
| 337 | - if (message.readableBytes() < minLength) { | ||
| 338 | - log.debug("BGP RX OPEN Error from {}: " + | ||
| 339 | - "Message length {} too short. Must be at least {}", | ||
| 340 | - remoteAddress, message.readableBytes(), minLength); | ||
| 341 | - // | ||
| 342 | - // ERROR: Bad Message Length | ||
| 343 | - // | ||
| 344 | - // Send NOTIFICATION and close the connection | ||
| 345 | - ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength( | ||
| 346 | - message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); | ||
| 347 | - ctx.getChannel().write(txMessage); | ||
| 348 | - closeSession(ctx); | ||
| 349 | - return; | ||
| 350 | - } | ||
| 351 | - | ||
| 352 | - // | ||
| 353 | - // Parse the OPEN message | ||
| 354 | - // | ||
| 355 | - // Remote BGP version | ||
| 356 | - remoteBgpVersion = message.readUnsignedByte(); | ||
| 357 | - if (remoteBgpVersion != BgpConstants.BGP_VERSION) { | ||
| 358 | - log.debug("BGP RX OPEN Error from {}: " + | ||
| 359 | - "Unsupported BGP version {}. Should be {}", | ||
| 360 | - remoteAddress, remoteBgpVersion, | ||
| 361 | - BgpConstants.BGP_VERSION); | ||
| 362 | - // | ||
| 363 | - // ERROR: Unsupported Version Number | ||
| 364 | - // | ||
| 365 | - // Send NOTIFICATION and close the connection | ||
| 366 | - int errorCode = OpenMessageError.ERROR_CODE; | ||
| 367 | - int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER; | ||
| 368 | - ChannelBuffer data = ChannelBuffers.buffer(2); | ||
| 369 | - data.writeShort(BgpConstants.BGP_VERSION); | ||
| 370 | - ChannelBuffer txMessage = | ||
| 371 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 372 | - ctx.getChannel().write(txMessage); | ||
| 373 | - closeSession(ctx); | ||
| 374 | - return; | ||
| 375 | - } | ||
| 376 | - | ||
| 377 | - // Remote AS number | ||
| 378 | - remoteAs = message.readUnsignedShort(); | ||
| 379 | - // | ||
| 380 | - // Verify that the AS number is same for all other BGP Sessions | ||
| 381 | - // NOTE: This check applies only for our use-case where all BGP | ||
| 382 | - // sessions are iBGP. | ||
| 383 | - // | ||
| 384 | - for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) { | ||
| 385 | - if (remoteAs != bgpSession.getRemoteAs()) { | ||
| 386 | - log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " + | ||
| 387 | - "Expected {}", | ||
| 388 | - remoteAddress, remoteAs, bgpSession.getRemoteAs()); | ||
| 389 | - // | ||
| 390 | - // ERROR: Bad Peer AS | ||
| 391 | - // | ||
| 392 | - // Send NOTIFICATION and close the connection | ||
| 393 | - int errorCode = OpenMessageError.ERROR_CODE; | ||
| 394 | - int errorSubcode = OpenMessageError.BAD_PEER_AS; | ||
| 395 | - ChannelBuffer txMessage = | ||
| 396 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 397 | - ctx.getChannel().write(txMessage); | ||
| 398 | - closeSession(ctx); | ||
| 399 | - return; | ||
| 400 | - } | ||
| 401 | - } | ||
| 402 | - | ||
| 403 | - // Remote Hold Time | ||
| 404 | - remoteHoldtime = message.readUnsignedShort(); | ||
| 405 | - if ((remoteHoldtime != 0) && | ||
| 406 | - (remoteHoldtime < BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME)) { | ||
| 407 | - log.debug("BGP RX OPEN Error from {}: " + | ||
| 408 | - "Unacceptable Hold Time field {}. " + | ||
| 409 | - "Should be 0 or at least {}", | ||
| 410 | - remoteAddress, remoteHoldtime, | ||
| 411 | - BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME); | ||
| 412 | - // | ||
| 413 | - // ERROR: Unacceptable Hold Time | ||
| 414 | - // | ||
| 415 | - // Send NOTIFICATION and close the connection | ||
| 416 | - int errorCode = OpenMessageError.ERROR_CODE; | ||
| 417 | - int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME; | ||
| 418 | - ChannelBuffer txMessage = | ||
| 419 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 420 | - ctx.getChannel().write(txMessage); | ||
| 421 | - closeSession(ctx); | ||
| 422 | - return; | ||
| 423 | - } | ||
| 424 | - | ||
| 425 | - // Remote BGP Identifier | ||
| 426 | - remoteBgpId = Ip4Address.valueOf((int) message.readUnsignedInt()); | ||
| 427 | - | ||
| 428 | - // Optional Parameters | ||
| 429 | - int optParamLen = message.readUnsignedByte(); | ||
| 430 | - if (message.readableBytes() < optParamLen) { | ||
| 431 | - log.debug("BGP RX OPEN Error from {}: " + | ||
| 432 | - "Invalid Optional Parameter Length field {}. " + | ||
| 433 | - "Remaining Optional Parameters {}", | ||
| 434 | - remoteAddress, optParamLen, message.readableBytes()); | ||
| 435 | - // | ||
| 436 | - // ERROR: Invalid Optional Parameter Length field: Unspecific | ||
| 437 | - // | ||
| 438 | - // Send NOTIFICATION and close the connection | ||
| 439 | - int errorCode = OpenMessageError.ERROR_CODE; | ||
| 440 | - int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC; | ||
| 441 | - ChannelBuffer txMessage = | ||
| 442 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 443 | - ctx.getChannel().write(txMessage); | ||
| 444 | - closeSession(ctx); | ||
| 445 | - return; | ||
| 446 | - } | ||
| 447 | - // NOTE: Parse the optional parameters (if needed) | ||
| 448 | - message.readBytes(optParamLen); // NOTE: data ignored | ||
| 449 | - | ||
| 450 | - // | ||
| 451 | - // Copy some of the remote peer's state/setup to the local setup: | ||
| 452 | - // - BGP version | ||
| 453 | - // - AS number (NOTE: the peer setup is always iBGP) | ||
| 454 | - // - Holdtime | ||
| 455 | - // Also, assign the local BGP ID based on the local setup | ||
| 456 | - // | ||
| 457 | - localBgpVersion = remoteBgpVersion; | ||
| 458 | - localAs = remoteAs; | ||
| 459 | - localHoldtime = remoteHoldtime; | ||
| 460 | - localBgpId = bgpSessionManager.getMyBgpId(); | ||
| 461 | - | ||
| 462 | - // Set the Keepalive interval | ||
| 463 | - if (localHoldtime == 0) { | ||
| 464 | - localKeepaliveInterval = 0; | ||
| 465 | - } else { | ||
| 466 | - localKeepaliveInterval = Math.max(localHoldtime / | ||
| 467 | - BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL, | ||
| 468 | - BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL); | ||
| 469 | - } | ||
| 470 | - | ||
| 471 | - log.debug("BGP RX OPEN message from {}: " + | ||
| 472 | - "BGPv{} AS {} BGP-ID {} Holdtime {}", | ||
| 473 | - remoteAddress, remoteBgpVersion, remoteAs, | ||
| 474 | - remoteBgpId, remoteHoldtime); | ||
| 475 | - | ||
| 476 | - // Send my OPEN followed by KEEPALIVE | ||
| 477 | - ChannelBuffer txMessage = prepareBgpOpen(); | ||
| 478 | - ctx.getChannel().write(txMessage); | ||
| 479 | - // | ||
| 480 | - txMessage = prepareBgpKeepalive(); | ||
| 481 | - ctx.getChannel().write(txMessage); | ||
| 482 | - | ||
| 483 | - // Start the KEEPALIVE timer | ||
| 484 | - restartKeepaliveTimer(ctx); | ||
| 485 | - | ||
| 486 | - // Start the Session Timeout timer | ||
| 487 | - restartSessionTimeoutTimer(ctx); | ||
| 488 | - } | ||
| 489 | - | ||
| 490 | - /** | ||
| 491 | - * Processes BGP UPDATE message. | ||
| 492 | - * | ||
| 493 | - * @param ctx the Channel Handler Context | ||
| 494 | - * @param message the message to process | ||
| 495 | - */ | ||
| 496 | - void processBgpUpdate(ChannelHandlerContext ctx, ChannelBuffer message) { | ||
| 497 | - Collection<BgpRouteEntry> addedRoutes = null; | ||
| 498 | - Map<Ip4Prefix, BgpRouteEntry> deletedRoutes = new HashMap<>(); | ||
| 499 | - | ||
| 500 | - int minLength = | ||
| 501 | - BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
| 502 | - if (message.readableBytes() < minLength) { | ||
| 503 | - log.debug("BGP RX UPDATE Error from {}: " + | ||
| 504 | - "Message length {} too short. Must be at least {}", | ||
| 505 | - remoteAddress, message.readableBytes(), minLength); | ||
| 506 | - // | ||
| 507 | - // ERROR: Bad Message Length | ||
| 508 | - // | ||
| 509 | - // Send NOTIFICATION and close the connection | ||
| 510 | - ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength( | ||
| 511 | - message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); | ||
| 512 | - ctx.getChannel().write(txMessage); | ||
| 513 | - closeSession(ctx); | ||
| 514 | - return; | ||
| 515 | - } | ||
| 516 | - | ||
| 517 | - log.debug("BGP RX UPDATE message from {}", remoteAddress); | ||
| 518 | - | ||
| 519 | - // | ||
| 520 | - // Parse the UPDATE message | ||
| 521 | - // | ||
| 522 | - | ||
| 523 | - // | ||
| 524 | - // Parse the Withdrawn Routes | ||
| 525 | - // | ||
| 526 | - int withdrawnRoutesLength = message.readUnsignedShort(); | ||
| 527 | - if (withdrawnRoutesLength > message.readableBytes()) { | ||
| 528 | - // ERROR: Malformed Attribute List | ||
| 529 | - actionsBgpUpdateMalformedAttributeList(ctx); | ||
| 530 | - return; | ||
| 531 | - } | ||
| 532 | - Collection<Ip4Prefix> withdrawnPrefixes = null; | ||
| 533 | - try { | ||
| 534 | - withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength, | ||
| 535 | - message); | ||
| 536 | - } catch (BgpParseException e) { | ||
| 537 | - // ERROR: Invalid Network Field | ||
| 538 | - log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ", | ||
| 539 | - remoteBgpId, e); | ||
| 540 | - actionsBgpUpdateInvalidNetworkField(ctx); | ||
| 541 | - return; | ||
| 542 | - } | ||
| 543 | - for (Ip4Prefix prefix : withdrawnPrefixes) { | ||
| 544 | - log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}", | ||
| 545 | - remoteAddress, prefix); | ||
| 546 | - BgpRouteEntry bgpRouteEntry = bgpRibIn.get(prefix); | ||
| 547 | - if (bgpRouteEntry != null) { | ||
| 548 | - deletedRoutes.put(prefix, bgpRouteEntry); | ||
| 549 | - } | ||
| 550 | - } | ||
| 551 | - | ||
| 552 | - // | ||
| 553 | - // Parse the Path Attributes | ||
| 554 | - // | ||
| 555 | - try { | ||
| 556 | - addedRoutes = parsePathAttributes(ctx, message); | ||
| 557 | - } catch (BgpParseException e) { | ||
| 558 | - log.debug("Exception parsing Path Attributes from BGP peer {}: ", | ||
| 559 | - remoteBgpId, e); | ||
| 560 | - // NOTE: The session was already closed, so nothing else to do | ||
| 561 | - return; | ||
| 562 | - } | ||
| 563 | - // Ignore WITHDRAWN routes that are ADDED | ||
| 564 | - for (BgpRouteEntry bgpRouteEntry : addedRoutes) { | ||
| 565 | - deletedRoutes.remove(bgpRouteEntry.prefix()); | ||
| 566 | - } | ||
| 567 | - | ||
| 568 | - // Update the BGP RIB-IN | ||
| 569 | - for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) { | ||
| 570 | - bgpRibIn.remove(bgpRouteEntry.prefix()); | ||
| 571 | - } | ||
| 572 | - for (BgpRouteEntry bgpRouteEntry : addedRoutes) { | ||
| 573 | - bgpRibIn.put(bgpRouteEntry.prefix(), bgpRouteEntry); | ||
| 574 | - } | ||
| 575 | - | ||
| 576 | - // Push the updates to the BGP Merged RIB | ||
| 577 | - BgpSessionManager.BgpRouteSelector bgpRouteSelector = | ||
| 578 | - bgpSessionManager.getBgpRouteSelector(); | ||
| 579 | - bgpRouteSelector.routeUpdates(this, addedRoutes, | ||
| 580 | - deletedRoutes.values()); | ||
| 581 | - | ||
| 582 | - // Start the Session Timeout timer | ||
| 583 | - restartSessionTimeoutTimer(ctx); | ||
| 584 | - } | ||
| 585 | - | ||
| 586 | - /** | ||
| 587 | - * Parse BGP Path Attributes from the BGP UPDATE message. | ||
| 588 | - * | ||
| 589 | - * @param ctx the Channel Handler Context | ||
| 590 | - * @param message the message to parse | ||
| 591 | - * @return a collection of the result BGP Route Entries | ||
| 592 | - * @throws BgpParseException | ||
| 593 | - */ | ||
| 594 | - private Collection<BgpRouteEntry> parsePathAttributes( | ||
| 595 | - ChannelHandlerContext ctx, | ||
| 596 | - ChannelBuffer message) | ||
| 597 | - throws BgpParseException { | ||
| 598 | - Map<Ip4Prefix, BgpRouteEntry> addedRoutes = new HashMap<>(); | ||
| 599 | - | ||
| 600 | - // | ||
| 601 | - // Parsed values | ||
| 602 | - // | ||
| 603 | - Short origin = -1; // Mandatory | ||
| 604 | - BgpRouteEntry.AsPath asPath = null; // Mandatory | ||
| 605 | - Ip4Address nextHop = null; // Mandatory | ||
| 606 | - long multiExitDisc = // Optional | ||
| 607 | - BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC; | ||
| 608 | - Long localPref = null; // Mandatory | ||
| 609 | - Long aggregatorAsNumber = null; // Optional: unused | ||
| 610 | - Ip4Address aggregatorIpAddress = null; // Optional: unused | ||
| 611 | - | ||
| 612 | - // | ||
| 613 | - // Get and verify the Path Attributes Length | ||
| 614 | - // | ||
| 615 | - int pathAttributeLength = message.readUnsignedShort(); | ||
| 616 | - if (pathAttributeLength > message.readableBytes()) { | ||
| 617 | - // ERROR: Malformed Attribute List | ||
| 618 | - actionsBgpUpdateMalformedAttributeList(ctx); | ||
| 619 | - String errorMsg = "Malformed Attribute List"; | ||
| 620 | - throw new BgpParseException(errorMsg); | ||
| 621 | - } | ||
| 622 | - if (pathAttributeLength == 0) { | ||
| 623 | - return addedRoutes.values(); | ||
| 624 | - } | ||
| 625 | - | ||
| 626 | - // | ||
| 627 | - // Parse the Path Attributes | ||
| 628 | - // | ||
| 629 | - int pathAttributeEnd = message.readerIndex() + pathAttributeLength; | ||
| 630 | - while (message.readerIndex() < pathAttributeEnd) { | ||
| 631 | - int attrFlags = message.readUnsignedByte(); | ||
| 632 | - if (message.readerIndex() >= pathAttributeEnd) { | ||
| 633 | - // ERROR: Malformed Attribute List | ||
| 634 | - actionsBgpUpdateMalformedAttributeList(ctx); | ||
| 635 | - String errorMsg = "Malformed Attribute List"; | ||
| 636 | - throw new BgpParseException(errorMsg); | ||
| 637 | - } | ||
| 638 | - int attrTypeCode = message.readUnsignedByte(); | ||
| 639 | - | ||
| 640 | - // The Attribute Flags | ||
| 641 | - boolean optionalBit = ((0x80 & attrFlags) != 0); | ||
| 642 | - boolean transitiveBit = ((0x40 & attrFlags) != 0); | ||
| 643 | - boolean partialBit = ((0x20 & attrFlags) != 0); | ||
| 644 | - boolean extendedLengthBit = ((0x10 & attrFlags) != 0); | ||
| 645 | - | ||
| 646 | - // The Attribute Length | ||
| 647 | - int attrLen = 0; | ||
| 648 | - int attrLenOctets = 1; | ||
| 649 | - if (extendedLengthBit) { | ||
| 650 | - attrLenOctets = 2; | ||
| 651 | - } | ||
| 652 | - if (message.readerIndex() + attrLenOctets > pathAttributeEnd) { | ||
| 653 | - // ERROR: Malformed Attribute List | ||
| 654 | - actionsBgpUpdateMalformedAttributeList(ctx); | ||
| 655 | - String errorMsg = "Malformed Attribute List"; | ||
| 656 | - throw new BgpParseException(errorMsg); | ||
| 657 | - } | ||
| 658 | - if (extendedLengthBit) { | ||
| 659 | - attrLen = message.readUnsignedShort(); | ||
| 660 | - } else { | ||
| 661 | - attrLen = message.readUnsignedByte(); | ||
| 662 | - } | ||
| 663 | - if (message.readerIndex() + attrLen > pathAttributeEnd) { | ||
| 664 | - // ERROR: Malformed Attribute List | ||
| 665 | - actionsBgpUpdateMalformedAttributeList(ctx); | ||
| 666 | - String errorMsg = "Malformed Attribute List"; | ||
| 667 | - throw new BgpParseException(errorMsg); | ||
| 668 | - } | ||
| 669 | - | ||
| 670 | - // | ||
| 671 | - // Verify the Attribute Flags | ||
| 672 | - // | ||
| 673 | - verifyBgpUpdateAttributeFlags(ctx, attrTypeCode, attrLen, | ||
| 674 | - attrFlags, message); | ||
| 675 | - | ||
| 676 | - // | ||
| 677 | - // Extract the Attribute Value based on the Attribute Type Code | ||
| 678 | - // | ||
| 679 | - switch (attrTypeCode) { | ||
| 680 | - | ||
| 681 | - case BgpConstants.Update.Origin.TYPE: | ||
| 682 | - // Attribute Type Code ORIGIN | ||
| 683 | - origin = parseAttributeTypeOrigin(ctx, attrTypeCode, attrLen, | ||
| 684 | - attrFlags, message); | ||
| 685 | - break; | ||
| 686 | - | ||
| 687 | - case BgpConstants.Update.AsPath.TYPE: | ||
| 688 | - // Attribute Type Code AS_PATH | ||
| 689 | - asPath = parseAttributeTypeAsPath(ctx, attrTypeCode, attrLen, | ||
| 690 | - attrFlags, message); | ||
| 691 | - break; | ||
| 692 | - | ||
| 693 | - case BgpConstants.Update.NextHop.TYPE: | ||
| 694 | - // Attribute Type Code NEXT_HOP | ||
| 695 | - nextHop = parseAttributeTypeNextHop(ctx, attrTypeCode, attrLen, | ||
| 696 | - attrFlags, message); | ||
| 697 | - break; | ||
| 698 | - | ||
| 699 | - case BgpConstants.Update.MultiExitDisc.TYPE: | ||
| 700 | - // Attribute Type Code MULTI_EXIT_DISC | ||
| 701 | - multiExitDisc = | ||
| 702 | - parseAttributeTypeMultiExitDisc(ctx, attrTypeCode, attrLen, | ||
| 703 | - attrFlags, message); | ||
| 704 | - break; | ||
| 705 | - | ||
| 706 | - case BgpConstants.Update.LocalPref.TYPE: | ||
| 707 | - // Attribute Type Code LOCAL_PREF | ||
| 708 | - localPref = | ||
| 709 | - parseAttributeTypeLocalPref(ctx, attrTypeCode, attrLen, | ||
| 710 | - attrFlags, message); | ||
| 711 | - break; | ||
| 712 | - | ||
| 713 | - case BgpConstants.Update.AtomicAggregate.TYPE: | ||
| 714 | - // Attribute Type Code ATOMIC_AGGREGATE | ||
| 715 | - parseAttributeTypeAtomicAggregate(ctx, attrTypeCode, attrLen, | ||
| 716 | - attrFlags, message); | ||
| 717 | - // Nothing to do: this attribute is primarily informational | ||
| 718 | - break; | ||
| 719 | - | ||
| 720 | - case BgpConstants.Update.Aggregator.TYPE: | ||
| 721 | - // Attribute Type Code AGGREGATOR | ||
| 722 | - Pair<Long, Ip4Address> aggregator = | ||
| 723 | - parseAttributeTypeAggregator(ctx, attrTypeCode, attrLen, | ||
| 724 | - attrFlags, message); | ||
| 725 | - aggregatorAsNumber = aggregator.getLeft(); | ||
| 726 | - aggregatorIpAddress = aggregator.getRight(); | ||
| 727 | - break; | ||
| 728 | - | ||
| 729 | - default: | ||
| 730 | - // NOTE: Parse any new Attribute Types if needed | ||
| 731 | - if (!optionalBit) { | ||
| 732 | - // ERROR: Unrecognized Well-known Attribute | ||
| 733 | - actionsBgpUpdateUnrecognizedWellKnownAttribute( | ||
| 734 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 735 | - String errorMsg = "Unrecognized Well-known Attribute: " + | ||
| 736 | - attrTypeCode; | ||
| 737 | - throw new BgpParseException(errorMsg); | ||
| 738 | - } | ||
| 739 | - | ||
| 740 | - // Skip the data from the unrecognized attribute | ||
| 741 | - log.debug("BGP RX UPDATE message from {}: " + | ||
| 742 | - "Unrecognized Attribute Type {}", | ||
| 743 | - remoteAddress, attrTypeCode); | ||
| 744 | - message.skipBytes(attrLen); | ||
| 745 | - break; | ||
| 746 | - } | ||
| 747 | - } | ||
| 748 | - | ||
| 749 | - // | ||
| 750 | - // Verify the Well-known Attributes | ||
| 751 | - // | ||
| 752 | - verifyBgpUpdateWellKnownAttributes(ctx, origin, asPath, nextHop, | ||
| 753 | - localPref); | ||
| 754 | - | ||
| 755 | - // | ||
| 756 | - // Parse the NLRI (Network Layer Reachability Information) | ||
| 757 | - // | ||
| 758 | - Collection<Ip4Prefix> addedPrefixes = null; | ||
| 759 | - int nlriLength = message.readableBytes(); | ||
| 760 | - try { | ||
| 761 | - addedPrefixes = parsePackedPrefixes(nlriLength, message); | ||
| 762 | - } catch (BgpParseException e) { | ||
| 763 | - // ERROR: Invalid Network Field | ||
| 764 | - log.debug("Exception parsing NLRI from BGP peer {}: ", | ||
| 765 | - remoteBgpId, e); | ||
| 766 | - actionsBgpUpdateInvalidNetworkField(ctx); | ||
| 767 | - // Rethrow the exception | ||
| 768 | - throw e; | ||
| 769 | - } | ||
| 770 | - | ||
| 771 | - // Generate the added routes | ||
| 772 | - for (Ip4Prefix prefix : addedPrefixes) { | ||
| 773 | - BgpRouteEntry bgpRouteEntry = | ||
| 774 | - new BgpRouteEntry(this, prefix, nextHop, | ||
| 775 | - origin.byteValue(), asPath, localPref); | ||
| 776 | - bgpRouteEntry.setMultiExitDisc(multiExitDisc); | ||
| 777 | - if (bgpRouteEntry.hasAsPathLoop(localAs)) { | ||
| 778 | - log.debug("BGP RX UPDATE message IGNORED from {}: {} " + | ||
| 779 | - "nextHop {}: contains AS Path loop", | ||
| 780 | - remoteAddress, prefix, nextHop); | ||
| 781 | - continue; | ||
| 782 | - } else { | ||
| 783 | - log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}", | ||
| 784 | - remoteAddress, prefix, nextHop); | ||
| 785 | - } | ||
| 786 | - addedRoutes.put(prefix, bgpRouteEntry); | ||
| 787 | - } | ||
| 788 | - | ||
| 789 | - return addedRoutes.values(); | ||
| 790 | - } | ||
| 791 | - | ||
| 792 | - /** | ||
| 793 | - * Verifies BGP UPDATE Well-known Attributes. | ||
| 794 | - * | ||
| 795 | - * @param ctx the Channel Handler Context | ||
| 796 | - * @param origin the ORIGIN well-known mandatory attribute | ||
| 797 | - * @param asPath the AS_PATH well-known mandatory attribute | ||
| 798 | - * @param nextHop the NEXT_HOP well-known mandatory attribute | ||
| 799 | - * @param localPref the LOCAL_PREF required attribute | ||
| 800 | - * @throws BgpParseException | ||
| 801 | - */ | ||
| 802 | - private void verifyBgpUpdateWellKnownAttributes( | ||
| 803 | - ChannelHandlerContext ctx, | ||
| 804 | - Short origin, | ||
| 805 | - BgpRouteEntry.AsPath asPath, | ||
| 806 | - Ip4Address nextHop, | ||
| 807 | - Long localPref) | ||
| 808 | - throws BgpParseException { | ||
| 809 | - // | ||
| 810 | - // Check for Missing Well-known Attributes | ||
| 811 | - // | ||
| 812 | - if ((origin == null) || (origin == -1)) { | ||
| 813 | - // Missing Attribute Type Code ORIGIN | ||
| 814 | - int type = BgpConstants.Update.Origin.TYPE; | ||
| 815 | - actionsBgpUpdateMissingWellKnownAttribute(ctx, type); | ||
| 816 | - String errorMsg = "Missing Well-known Attribute: ORIGIN"; | ||
| 817 | - throw new BgpParseException(errorMsg); | ||
| 818 | - } | ||
| 819 | - if (asPath == null) { | ||
| 820 | - // Missing Attribute Type Code AS_PATH | ||
| 821 | - int type = BgpConstants.Update.AsPath.TYPE; | ||
| 822 | - actionsBgpUpdateMissingWellKnownAttribute(ctx, type); | ||
| 823 | - String errorMsg = "Missing Well-known Attribute: AS_PATH"; | ||
| 824 | - throw new BgpParseException(errorMsg); | ||
| 825 | - } | ||
| 826 | - if (nextHop == null) { | ||
| 827 | - // Missing Attribute Type Code NEXT_HOP | ||
| 828 | - int type = BgpConstants.Update.NextHop.TYPE; | ||
| 829 | - actionsBgpUpdateMissingWellKnownAttribute(ctx, type); | ||
| 830 | - String errorMsg = "Missing Well-known Attribute: NEXT_HOP"; | ||
| 831 | - throw new BgpParseException(errorMsg); | ||
| 832 | - } | ||
| 833 | - if (localPref == null) { | ||
| 834 | - // Missing Attribute Type Code LOCAL_PREF | ||
| 835 | - // NOTE: Required for iBGP | ||
| 836 | - int type = BgpConstants.Update.LocalPref.TYPE; | ||
| 837 | - actionsBgpUpdateMissingWellKnownAttribute(ctx, type); | ||
| 838 | - String errorMsg = "Missing Well-known Attribute: LOCAL_PREF"; | ||
| 839 | - throw new BgpParseException(errorMsg); | ||
| 840 | - } | ||
| 841 | - } | ||
| 842 | - | ||
| 843 | - /** | ||
| 844 | - * Verifies the BGP UPDATE Attribute Flags. | ||
| 845 | - * | ||
| 846 | - * @param ctx the Channel Handler Context | ||
| 847 | - * @param attrTypeCode the attribute type code | ||
| 848 | - * @param attrLen the attribute length (in octets) | ||
| 849 | - * @param attrFlags the attribute flags | ||
| 850 | - * @param message the message to parse | ||
| 851 | - * @throws BgpParseException | ||
| 852 | - */ | ||
| 853 | - private void verifyBgpUpdateAttributeFlags( | ||
| 854 | - ChannelHandlerContext ctx, | ||
| 855 | - int attrTypeCode, | ||
| 856 | - int attrLen, | ||
| 857 | - int attrFlags, | ||
| 858 | - ChannelBuffer message) | ||
| 859 | - throws BgpParseException { | ||
| 860 | - | ||
| 861 | - // | ||
| 862 | - // Assign the Attribute Type Name and the Well-known flag | ||
| 863 | - // | ||
| 864 | - String typeName = "UNKNOWN"; | ||
| 865 | - boolean isWellKnown = false; | ||
| 866 | - switch (attrTypeCode) { | ||
| 867 | - case BgpConstants.Update.Origin.TYPE: | ||
| 868 | - isWellKnown = true; | ||
| 869 | - typeName = "ORIGIN"; | ||
| 870 | - break; | ||
| 871 | - case BgpConstants.Update.AsPath.TYPE: | ||
| 872 | - isWellKnown = true; | ||
| 873 | - typeName = "AS_PATH"; | ||
| 874 | - break; | ||
| 875 | - case BgpConstants.Update.NextHop.TYPE: | ||
| 876 | - isWellKnown = true; | ||
| 877 | - typeName = "NEXT_HOP"; | ||
| 878 | - break; | ||
| 879 | - case BgpConstants.Update.MultiExitDisc.TYPE: | ||
| 880 | - isWellKnown = false; | ||
| 881 | - typeName = "MULTI_EXIT_DISC"; | ||
| 882 | - break; | ||
| 883 | - case BgpConstants.Update.LocalPref.TYPE: | ||
| 884 | - isWellKnown = true; | ||
| 885 | - typeName = "LOCAL_PREF"; | ||
| 886 | - break; | ||
| 887 | - case BgpConstants.Update.AtomicAggregate.TYPE: | ||
| 888 | - isWellKnown = true; | ||
| 889 | - typeName = "ATOMIC_AGGREGATE"; | ||
| 890 | - break; | ||
| 891 | - case BgpConstants.Update.Aggregator.TYPE: | ||
| 892 | - isWellKnown = false; | ||
| 893 | - typeName = "AGGREGATOR"; | ||
| 894 | - break; | ||
| 895 | - default: | ||
| 896 | - isWellKnown = false; | ||
| 897 | - typeName = "UNKNOWN(" + attrTypeCode + ")"; | ||
| 898 | - break; | ||
| 899 | - } | ||
| 900 | - | ||
| 901 | - // | ||
| 902 | - // Verify the Attribute Flags | ||
| 903 | - // | ||
| 904 | - boolean optionalBit = ((0x80 & attrFlags) != 0); | ||
| 905 | - boolean transitiveBit = ((0x40 & attrFlags) != 0); | ||
| 906 | - boolean partialBit = ((0x20 & attrFlags) != 0); | ||
| 907 | - if ((isWellKnown && optionalBit) || | ||
| 908 | - (isWellKnown && (!transitiveBit)) || | ||
| 909 | - (isWellKnown && partialBit) || | ||
| 910 | - (optionalBit && (!transitiveBit) && partialBit)) { | ||
| 911 | - // | ||
| 912 | - // ERROR: The Optional bit cannot be set for Well-known attributes | ||
| 913 | - // ERROR: The Transtive bit MUST be 1 for well-known attributes | ||
| 914 | - // ERROR: The Partial bit MUST be 0 for well-known attributes | ||
| 915 | - // ERROR: The Partial bit MUST be 0 for optional non-transitive | ||
| 916 | - // attributes | ||
| 917 | - // | ||
| 918 | - actionsBgpUpdateAttributeFlagsError( | ||
| 919 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 920 | - String errorMsg = "Attribute Flags Error for " + typeName + ": " + | ||
| 921 | - attrFlags; | ||
| 922 | - throw new BgpParseException(errorMsg); | ||
| 923 | - } | ||
| 924 | - } | ||
| 925 | - | ||
| 926 | - /** | ||
| 927 | - * Parses BGP UPDATE Attribute Type ORIGIN. | ||
| 928 | - * | ||
| 929 | - * @param ctx the Channel Handler Context | ||
| 930 | - * @param attrTypeCode the attribute type code | ||
| 931 | - * @param attrLen the attribute length (in octets) | ||
| 932 | - * @param attrFlags the attribute flags | ||
| 933 | - * @param message the message to parse | ||
| 934 | - * @return the parsed ORIGIN value | ||
| 935 | - * @throws BgpParseException | ||
| 936 | - */ | ||
| 937 | - private short parseAttributeTypeOrigin( | ||
| 938 | - ChannelHandlerContext ctx, | ||
| 939 | - int attrTypeCode, | ||
| 940 | - int attrLen, | ||
| 941 | - int attrFlags, | ||
| 942 | - ChannelBuffer message) | ||
| 943 | - throws BgpParseException { | ||
| 944 | - | ||
| 945 | - // Check the Attribute Length | ||
| 946 | - if (attrLen != BgpConstants.Update.Origin.LENGTH) { | ||
| 947 | - // ERROR: Attribute Length Error | ||
| 948 | - actionsBgpUpdateAttributeLengthError( | ||
| 949 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 950 | - String errorMsg = "Attribute Length Error"; | ||
| 951 | - throw new BgpParseException(errorMsg); | ||
| 952 | - } | ||
| 953 | - | ||
| 954 | - message.markReaderIndex(); | ||
| 955 | - short origin = message.readUnsignedByte(); | ||
| 956 | - switch (origin) { | ||
| 957 | - case BgpConstants.Update.Origin.IGP: | ||
| 958 | - // FALLTHROUGH | ||
| 959 | - case BgpConstants.Update.Origin.EGP: | ||
| 960 | - // FALLTHROUGH | ||
| 961 | - case BgpConstants.Update.Origin.INCOMPLETE: | ||
| 962 | - break; | ||
| 963 | - default: | ||
| 964 | - // ERROR: Invalid ORIGIN Attribute | ||
| 965 | - message.resetReaderIndex(); | ||
| 966 | - actionsBgpUpdateInvalidOriginAttribute( | ||
| 967 | - ctx, attrTypeCode, attrLen, attrFlags, message, origin); | ||
| 968 | - String errorMsg = "Invalid ORIGIN Attribute: " + origin; | ||
| 969 | - throw new BgpParseException(errorMsg); | ||
| 970 | - } | ||
| 971 | - | ||
| 972 | - return origin; | ||
| 973 | - } | ||
| 974 | - | ||
| 975 | - /** | ||
| 976 | - * Parses BGP UPDATE Attribute AS Path. | ||
| 977 | - * | ||
| 978 | - * @param ctx the Channel Handler Context | ||
| 979 | - * @param attrTypeCode the attribute type code | ||
| 980 | - * @param attrLen the attribute length (in octets) | ||
| 981 | - * @param attrFlags the attribute flags | ||
| 982 | - * @param message the message to parse | ||
| 983 | - * @return the parsed AS Path | ||
| 984 | - * @throws BgpParseException | ||
| 985 | - */ | ||
| 986 | - private BgpRouteEntry.AsPath parseAttributeTypeAsPath( | ||
| 987 | - ChannelHandlerContext ctx, | ||
| 988 | - int attrTypeCode, | ||
| 989 | - int attrLen, | ||
| 990 | - int attrFlags, | ||
| 991 | - ChannelBuffer message) | ||
| 992 | - throws BgpParseException { | ||
| 993 | - ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>(); | ||
| 994 | - | ||
| 995 | - // | ||
| 996 | - // Parse the message | ||
| 997 | - // | ||
| 998 | - while (attrLen > 0) { | ||
| 999 | - if (attrLen < 2) { | ||
| 1000 | - // ERROR: Malformed AS_PATH | ||
| 1001 | - actionsBgpUpdateMalformedAsPath(ctx); | ||
| 1002 | - String errorMsg = "Malformed AS Path"; | ||
| 1003 | - throw new BgpParseException(errorMsg); | ||
| 1004 | - } | ||
| 1005 | - // Get the Path Segment Type and Length (in number of ASes) | ||
| 1006 | - short pathSegmentType = message.readUnsignedByte(); | ||
| 1007 | - short pathSegmentLength = message.readUnsignedByte(); | ||
| 1008 | - attrLen -= 2; | ||
| 1009 | - | ||
| 1010 | - // Verify the Path Segment Type | ||
| 1011 | - switch (pathSegmentType) { | ||
| 1012 | - case BgpConstants.Update.AsPath.AS_SET: | ||
| 1013 | - // FALLTHROUGH | ||
| 1014 | - case BgpConstants.Update.AsPath.AS_SEQUENCE: | ||
| 1015 | - // FALLTHROUGH | ||
| 1016 | - case BgpConstants.Update.AsPath.AS_CONFED_SEQUENCE: | ||
| 1017 | - // FALLTHROUGH | ||
| 1018 | - case BgpConstants.Update.AsPath.AS_CONFED_SET: | ||
| 1019 | - break; | ||
| 1020 | - default: | ||
| 1021 | - // ERROR: Invalid Path Segment Type | ||
| 1022 | - // | ||
| 1023 | - // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode | ||
| 1024 | - // for "Invalid Path Segment Type", hence we return | ||
| 1025 | - // the error as "Malformed AS_PATH". | ||
| 1026 | - // | ||
| 1027 | - actionsBgpUpdateMalformedAsPath(ctx); | ||
| 1028 | - String errorMsg = | ||
| 1029 | - "Invalid AS Path Segment Type: " + pathSegmentType; | ||
| 1030 | - throw new BgpParseException(errorMsg); | ||
| 1031 | - } | ||
| 1032 | - | ||
| 1033 | - // Parse the AS numbers | ||
| 1034 | - if (2 * pathSegmentLength > attrLen) { | ||
| 1035 | - // ERROR: Malformed AS_PATH | ||
| 1036 | - actionsBgpUpdateMalformedAsPath(ctx); | ||
| 1037 | - String errorMsg = "Malformed AS Path"; | ||
| 1038 | - throw new BgpParseException(errorMsg); | ||
| 1039 | - } | ||
| 1040 | - attrLen -= (2 * pathSegmentLength); | ||
| 1041 | - ArrayList<Long> segmentAsNumbers = new ArrayList<>(); | ||
| 1042 | - while (pathSegmentLength-- > 0) { | ||
| 1043 | - long asNumber = message.readUnsignedShort(); | ||
| 1044 | - segmentAsNumbers.add(asNumber); | ||
| 1045 | - } | ||
| 1046 | - | ||
| 1047 | - BgpRouteEntry.PathSegment pathSegment = | ||
| 1048 | - new BgpRouteEntry.PathSegment((byte) pathSegmentType, | ||
| 1049 | - segmentAsNumbers); | ||
| 1050 | - pathSegments.add(pathSegment); | ||
| 1051 | - } | ||
| 1052 | - | ||
| 1053 | - return new BgpRouteEntry.AsPath(pathSegments); | ||
| 1054 | - } | ||
| 1055 | - | ||
| 1056 | - /** | ||
| 1057 | - * Parses BGP UPDATE Attribute Type NEXT_HOP. | ||
| 1058 | - * | ||
| 1059 | - * @param ctx the Channel Handler Context | ||
| 1060 | - * @param attrTypeCode the attribute type code | ||
| 1061 | - * @param attrLen the attribute length (in octets) | ||
| 1062 | - * @param attrFlags the attribute flags | ||
| 1063 | - * @param message the message to parse | ||
| 1064 | - * @return the parsed NEXT_HOP value | ||
| 1065 | - * @throws BgpParseException | ||
| 1066 | - */ | ||
| 1067 | - private Ip4Address parseAttributeTypeNextHop( | ||
| 1068 | - ChannelHandlerContext ctx, | ||
| 1069 | - int attrTypeCode, | ||
| 1070 | - int attrLen, | ||
| 1071 | - int attrFlags, | ||
| 1072 | - ChannelBuffer message) | ||
| 1073 | - throws BgpParseException { | ||
| 1074 | - | ||
| 1075 | - // Check the Attribute Length | ||
| 1076 | - if (attrLen != BgpConstants.Update.NextHop.LENGTH) { | ||
| 1077 | - // ERROR: Attribute Length Error | ||
| 1078 | - actionsBgpUpdateAttributeLengthError( | ||
| 1079 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 1080 | - String errorMsg = "Attribute Length Error"; | ||
| 1081 | - throw new BgpParseException(errorMsg); | ||
| 1082 | - } | ||
| 1083 | - | ||
| 1084 | - message.markReaderIndex(); | ||
| 1085 | - Ip4Address nextHopAddress = | ||
| 1086 | - Ip4Address.valueOf((int) message.readUnsignedInt()); | ||
| 1087 | - // | ||
| 1088 | - // Check whether the NEXT_HOP IP address is semantically correct. | ||
| 1089 | - // As per RFC 4271, Section 6.3: | ||
| 1090 | - // | ||
| 1091 | - // a) It MUST NOT be the IP address of the receiving speaker | ||
| 1092 | - // b) In the case of an EBGP .... | ||
| 1093 | - // | ||
| 1094 | - // Here we check only (a), because (b) doesn't apply for us: all our | ||
| 1095 | - // peers are iBGP. | ||
| 1096 | - // | ||
| 1097 | - if (nextHopAddress.equals(localIp4Address)) { | ||
| 1098 | - // ERROR: Invalid NEXT_HOP Attribute | ||
| 1099 | - message.resetReaderIndex(); | ||
| 1100 | - actionsBgpUpdateInvalidNextHopAttribute( | ||
| 1101 | - ctx, attrTypeCode, attrLen, attrFlags, message, | ||
| 1102 | - nextHopAddress); | ||
| 1103 | - String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress; | ||
| 1104 | - throw new BgpParseException(errorMsg); | ||
| 1105 | - } | ||
| 1106 | - | ||
| 1107 | - return nextHopAddress; | ||
| 1108 | - } | ||
| 1109 | - | ||
| 1110 | - /** | ||
| 1111 | - * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC. | ||
| 1112 | - * | ||
| 1113 | - * @param ctx the Channel Handler Context | ||
| 1114 | - * @param attrTypeCode the attribute type code | ||
| 1115 | - * @param attrLen the attribute length (in octets) | ||
| 1116 | - * @param attrFlags the attribute flags | ||
| 1117 | - * @param message the message to parse | ||
| 1118 | - * @return the parsed MULTI_EXIT_DISC value | ||
| 1119 | - * @throws BgpParseException | ||
| 1120 | - */ | ||
| 1121 | - private long parseAttributeTypeMultiExitDisc( | ||
| 1122 | - ChannelHandlerContext ctx, | ||
| 1123 | - int attrTypeCode, | ||
| 1124 | - int attrLen, | ||
| 1125 | - int attrFlags, | ||
| 1126 | - ChannelBuffer message) | ||
| 1127 | - throws BgpParseException { | ||
| 1128 | - | ||
| 1129 | - // Check the Attribute Length | ||
| 1130 | - if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) { | ||
| 1131 | - // ERROR: Attribute Length Error | ||
| 1132 | - actionsBgpUpdateAttributeLengthError( | ||
| 1133 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 1134 | - String errorMsg = "Attribute Length Error"; | ||
| 1135 | - throw new BgpParseException(errorMsg); | ||
| 1136 | - } | ||
| 1137 | - | ||
| 1138 | - long multiExitDisc = message.readUnsignedInt(); | ||
| 1139 | - return multiExitDisc; | ||
| 1140 | - } | ||
| 1141 | - | ||
| 1142 | - /** | ||
| 1143 | - * Parses BGP UPDATE Attribute Type LOCAL_PREF. | ||
| 1144 | - * | ||
| 1145 | - * @param ctx the Channel Handler Context | ||
| 1146 | - * @param attrTypeCode the attribute type code | ||
| 1147 | - * @param attrLen the attribute length (in octets) | ||
| 1148 | - * @param attrFlags the attribute flags | ||
| 1149 | - * @param message the message to parse | ||
| 1150 | - * @return the parsed LOCAL_PREF value | ||
| 1151 | - * @throws BgpParseException | ||
| 1152 | - */ | ||
| 1153 | - private long parseAttributeTypeLocalPref( | ||
| 1154 | - ChannelHandlerContext ctx, | ||
| 1155 | - int attrTypeCode, | ||
| 1156 | - int attrLen, | ||
| 1157 | - int attrFlags, | ||
| 1158 | - ChannelBuffer message) | ||
| 1159 | - throws BgpParseException { | ||
| 1160 | - | ||
| 1161 | - // Check the Attribute Length | ||
| 1162 | - if (attrLen != BgpConstants.Update.LocalPref.LENGTH) { | ||
| 1163 | - // ERROR: Attribute Length Error | ||
| 1164 | - actionsBgpUpdateAttributeLengthError( | ||
| 1165 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 1166 | - String errorMsg = "Attribute Length Error"; | ||
| 1167 | - throw new BgpParseException(errorMsg); | ||
| 1168 | - } | ||
| 1169 | - | ||
| 1170 | - long localPref = message.readUnsignedInt(); | ||
| 1171 | - return localPref; | ||
| 1172 | - } | ||
| 1173 | - | ||
| 1174 | - /** | ||
| 1175 | - * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE. | ||
| 1176 | - * | ||
| 1177 | - * @param ctx the Channel Handler Context | ||
| 1178 | - * @param attrTypeCode the attribute type code | ||
| 1179 | - * @param attrLen the attribute length (in octets) | ||
| 1180 | - * @param attrFlags the attribute flags | ||
| 1181 | - * @param message the message to parse | ||
| 1182 | - * @throws BgpParseException | ||
| 1183 | - */ | ||
| 1184 | - private void parseAttributeTypeAtomicAggregate( | ||
| 1185 | - ChannelHandlerContext ctx, | ||
| 1186 | - int attrTypeCode, | ||
| 1187 | - int attrLen, | ||
| 1188 | - int attrFlags, | ||
| 1189 | - ChannelBuffer message) | ||
| 1190 | - throws BgpParseException { | ||
| 1191 | - | ||
| 1192 | - // Check the Attribute Length | ||
| 1193 | - if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) { | ||
| 1194 | - // ERROR: Attribute Length Error | ||
| 1195 | - actionsBgpUpdateAttributeLengthError( | ||
| 1196 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 1197 | - String errorMsg = "Attribute Length Error"; | ||
| 1198 | - throw new BgpParseException(errorMsg); | ||
| 1199 | - } | ||
| 1200 | - | ||
| 1201 | - // Nothing to do: this attribute is primarily informational | ||
| 1202 | - } | ||
| 1203 | - | ||
| 1204 | - /** | ||
| 1205 | - * Parses BGP UPDATE Attribute Type AGGREGATOR. | ||
| 1206 | - * | ||
| 1207 | - * @param ctx the Channel Handler Context | ||
| 1208 | - * @param attrTypeCode the attribute type code | ||
| 1209 | - * @param attrLen the attribute length (in octets) | ||
| 1210 | - * @param attrFlags the attribute flags | ||
| 1211 | - * @param message the message to parse | ||
| 1212 | - * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address> | ||
| 1213 | - * @throws BgpParseException | ||
| 1214 | - */ | ||
| 1215 | - private Pair<Long, Ip4Address> parseAttributeTypeAggregator( | ||
| 1216 | - ChannelHandlerContext ctx, | ||
| 1217 | - int attrTypeCode, | ||
| 1218 | - int attrLen, | ||
| 1219 | - int attrFlags, | ||
| 1220 | - ChannelBuffer message) | ||
| 1221 | - throws BgpParseException { | ||
| 1222 | - | ||
| 1223 | - // Check the Attribute Length | ||
| 1224 | - if (attrLen != BgpConstants.Update.Aggregator.LENGTH) { | ||
| 1225 | - // ERROR: Attribute Length Error | ||
| 1226 | - actionsBgpUpdateAttributeLengthError( | ||
| 1227 | - ctx, attrTypeCode, attrLen, attrFlags, message); | ||
| 1228 | - String errorMsg = "Attribute Length Error"; | ||
| 1229 | - throw new BgpParseException(errorMsg); | ||
| 1230 | - } | ||
| 1231 | - | ||
| 1232 | - // The AGGREGATOR AS number | ||
| 1233 | - long aggregatorAsNumber = message.readUnsignedShort(); | ||
| 1234 | - // The AGGREGATOR IP address | ||
| 1235 | - Ip4Address aggregatorIpAddress = | ||
| 1236 | - Ip4Address.valueOf((int) message.readUnsignedInt()); | ||
| 1237 | - | ||
| 1238 | - Pair<Long, Ip4Address> aggregator = Pair.of(aggregatorAsNumber, | ||
| 1239 | - aggregatorIpAddress); | ||
| 1240 | - return aggregator; | ||
| 1241 | - } | ||
| 1242 | - | ||
| 1243 | - /** | ||
| 1244 | - * Parses a message that contains encoded IPv4 network prefixes. | ||
| 1245 | - * <p> | ||
| 1246 | - * The IPv4 prefixes are encoded in the form: | ||
| 1247 | - * <Length, Prefix> where Length is the length in bits of the IPv4 prefix, | ||
| 1248 | - * and Prefix is the IPv4 prefix (padded with trailing bits to the end | ||
| 1249 | - * of an octet). | ||
| 1250 | - * | ||
| 1251 | - * @param totalLength the total length of the data to parse | ||
| 1252 | - * @param message the message with data to parse | ||
| 1253 | - * @return a collection of parsed IPv4 network prefixes | ||
| 1254 | - * @throws BgpParseException | ||
| 1255 | - */ | ||
| 1256 | - private Collection<Ip4Prefix> parsePackedPrefixes(int totalLength, | ||
| 1257 | - ChannelBuffer message) | ||
| 1258 | - throws BgpParseException { | ||
| 1259 | - Collection<Ip4Prefix> result = new ArrayList<>(); | ||
| 1260 | - | ||
| 1261 | - if (totalLength == 0) { | ||
| 1262 | - return result; | ||
| 1263 | - } | ||
| 1264 | - | ||
| 1265 | - // Parse the data | ||
| 1266 | - int dataEnd = message.readerIndex() + totalLength; | ||
| 1267 | - while (message.readerIndex() < dataEnd) { | ||
| 1268 | - int prefixBitlen = message.readUnsignedByte(); | ||
| 1269 | - int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up | ||
| 1270 | - if (message.readerIndex() + prefixBytelen > dataEnd) { | ||
| 1271 | - String errorMsg = "Malformed Network Prefixes"; | ||
| 1272 | - throw new BgpParseException(errorMsg); | ||
| 1273 | - } | ||
| 1274 | - | ||
| 1275 | - long address = 0; | ||
| 1276 | - long extraShift = (4 - prefixBytelen) * 8; | ||
| 1277 | - while (prefixBytelen > 0) { | ||
| 1278 | - address <<= 8; | ||
| 1279 | - address |= message.readUnsignedByte(); | ||
| 1280 | - prefixBytelen--; | ||
| 1281 | - } | ||
| 1282 | - address <<= extraShift; | ||
| 1283 | - Ip4Prefix prefix = | ||
| 1284 | - Ip4Prefix.valueOf(Ip4Address.valueOf((int) address), | ||
| 1285 | - prefixBitlen); | ||
| 1286 | - result.add(prefix); | ||
| 1287 | - } | ||
| 1288 | - | ||
| 1289 | - return result; | ||
| 1290 | - } | ||
| 1291 | - | ||
| 1292 | - /** | ||
| 1293 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1294 | - * Invalid Network Field Error: send NOTIFICATION and close the channel. | ||
| 1295 | - * | ||
| 1296 | - * @param ctx the Channel Handler Context | ||
| 1297 | - */ | ||
| 1298 | - private void actionsBgpUpdateInvalidNetworkField( | ||
| 1299 | - ChannelHandlerContext ctx) { | ||
| 1300 | - log.debug("BGP RX UPDATE Error from {}: Invalid Network Field", | ||
| 1301 | - remoteAddress); | ||
| 1302 | - | ||
| 1303 | - // | ||
| 1304 | - // ERROR: Invalid Network Field | ||
| 1305 | - // | ||
| 1306 | - // Send NOTIFICATION and close the connection | ||
| 1307 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1308 | - int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD; | ||
| 1309 | - ChannelBuffer txMessage = | ||
| 1310 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 1311 | - ctx.getChannel().write(txMessage); | ||
| 1312 | - closeSession(ctx); | ||
| 1313 | - } | ||
| 1314 | - | ||
| 1315 | - /** | ||
| 1316 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1317 | - * Malformed Attribute List Error: send NOTIFICATION and close the channel. | ||
| 1318 | - * | ||
| 1319 | - * @param ctx the Channel Handler Context | ||
| 1320 | - */ | ||
| 1321 | - private void actionsBgpUpdateMalformedAttributeList( | ||
| 1322 | - ChannelHandlerContext ctx) { | ||
| 1323 | - log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List", | ||
| 1324 | - remoteAddress); | ||
| 1325 | - | ||
| 1326 | - // | ||
| 1327 | - // ERROR: Malformed Attribute List | ||
| 1328 | - // | ||
| 1329 | - // Send NOTIFICATION and close the connection | ||
| 1330 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1331 | - int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST; | ||
| 1332 | - ChannelBuffer txMessage = | ||
| 1333 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 1334 | - ctx.getChannel().write(txMessage); | ||
| 1335 | - closeSession(ctx); | ||
| 1336 | - } | ||
| 1337 | - | ||
| 1338 | - /** | ||
| 1339 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1340 | - * Missing Well-known Attribute Error: send NOTIFICATION and close the | ||
| 1341 | - * channel. | ||
| 1342 | - * | ||
| 1343 | - * @param ctx the Channel Handler Context | ||
| 1344 | - * @param missingAttrTypeCode the missing attribute type code | ||
| 1345 | - */ | ||
| 1346 | - private void actionsBgpUpdateMissingWellKnownAttribute( | ||
| 1347 | - ChannelHandlerContext ctx, | ||
| 1348 | - int missingAttrTypeCode) { | ||
| 1349 | - log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}", | ||
| 1350 | - remoteAddress, missingAttrTypeCode); | ||
| 1351 | - | ||
| 1352 | - // | ||
| 1353 | - // ERROR: Missing Well-known Attribute | ||
| 1354 | - // | ||
| 1355 | - // Send NOTIFICATION and close the connection | ||
| 1356 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1357 | - int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE; | ||
| 1358 | - ChannelBuffer data = ChannelBuffers.buffer(1); | ||
| 1359 | - data.writeByte(missingAttrTypeCode); | ||
| 1360 | - ChannelBuffer txMessage = | ||
| 1361 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1362 | - ctx.getChannel().write(txMessage); | ||
| 1363 | - closeSession(ctx); | ||
| 1364 | - } | ||
| 1365 | - | ||
| 1366 | - /** | ||
| 1367 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1368 | - * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel. | ||
| 1369 | - * | ||
| 1370 | - * @param ctx the Channel Handler Context | ||
| 1371 | - * @param attrTypeCode the attribute type code | ||
| 1372 | - * @param attrLen the attribute length (in octets) | ||
| 1373 | - * @param attrFlags the attribute flags | ||
| 1374 | - * @param message the message with the data | ||
| 1375 | - * @param origin the ORIGIN attribute value | ||
| 1376 | - */ | ||
| 1377 | - private void actionsBgpUpdateInvalidOriginAttribute( | ||
| 1378 | - ChannelHandlerContext ctx, | ||
| 1379 | - int attrTypeCode, | ||
| 1380 | - int attrLen, | ||
| 1381 | - int attrFlags, | ||
| 1382 | - ChannelBuffer message, | ||
| 1383 | - short origin) { | ||
| 1384 | - log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute", | ||
| 1385 | - remoteAddress); | ||
| 1386 | - | ||
| 1387 | - // | ||
| 1388 | - // ERROR: Invalid ORIGIN Attribute | ||
| 1389 | - // | ||
| 1390 | - // Send NOTIFICATION and close the connection | ||
| 1391 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1392 | - int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE; | ||
| 1393 | - ChannelBuffer data = | ||
| 1394 | - prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen, | ||
| 1395 | - attrFlags, message); | ||
| 1396 | - ChannelBuffer txMessage = | ||
| 1397 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1398 | - ctx.getChannel().write(txMessage); | ||
| 1399 | - closeSession(ctx); | ||
| 1400 | - } | ||
| 1401 | - | ||
| 1402 | - /** | ||
| 1403 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1404 | - * Attribute Flags Error: send NOTIFICATION and close the channel. | ||
| 1405 | - * | ||
| 1406 | - * @param ctx the Channel Handler Context | ||
| 1407 | - * @param attrTypeCode the attribute type code | ||
| 1408 | - * @param attrLen the attribute length (in octets) | ||
| 1409 | - * @param attrFlags the attribute flags | ||
| 1410 | - * @param message the message with the data | ||
| 1411 | - */ | ||
| 1412 | - private void actionsBgpUpdateAttributeFlagsError( | ||
| 1413 | - ChannelHandlerContext ctx, | ||
| 1414 | - int attrTypeCode, | ||
| 1415 | - int attrLen, | ||
| 1416 | - int attrFlags, | ||
| 1417 | - ChannelBuffer message) { | ||
| 1418 | - log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error", | ||
| 1419 | - remoteAddress); | ||
| 1420 | - | ||
| 1421 | - // | ||
| 1422 | - // ERROR: Attribute Flags Error | ||
| 1423 | - // | ||
| 1424 | - // Send NOTIFICATION and close the connection | ||
| 1425 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1426 | - int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR; | ||
| 1427 | - ChannelBuffer data = | ||
| 1428 | - prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen, | ||
| 1429 | - attrFlags, message); | ||
| 1430 | - ChannelBuffer txMessage = | ||
| 1431 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1432 | - ctx.getChannel().write(txMessage); | ||
| 1433 | - closeSession(ctx); | ||
| 1434 | - } | ||
| 1435 | - | ||
| 1436 | - /** | ||
| 1437 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1438 | - * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the | ||
| 1439 | - * channel. | ||
| 1440 | - * | ||
| 1441 | - * @param ctx the Channel Handler Context | ||
| 1442 | - * @param attrTypeCode the attribute type code | ||
| 1443 | - * @param attrLen the attribute length (in octets) | ||
| 1444 | - * @param attrFlags the attribute flags | ||
| 1445 | - * @param message the message with the data | ||
| 1446 | - * @param nextHop the NEXT_HOP attribute value | ||
| 1447 | - */ | ||
| 1448 | - private void actionsBgpUpdateInvalidNextHopAttribute( | ||
| 1449 | - ChannelHandlerContext ctx, | ||
| 1450 | - int attrTypeCode, | ||
| 1451 | - int attrLen, | ||
| 1452 | - int attrFlags, | ||
| 1453 | - ChannelBuffer message, | ||
| 1454 | - Ip4Address nextHop) { | ||
| 1455 | - log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}", | ||
| 1456 | - remoteAddress, nextHop); | ||
| 1457 | - | ||
| 1458 | - // | ||
| 1459 | - // ERROR: Invalid ORIGIN Attribute | ||
| 1460 | - // | ||
| 1461 | - // Send NOTIFICATION and close the connection | ||
| 1462 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1463 | - int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE; | ||
| 1464 | - ChannelBuffer data = | ||
| 1465 | - prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen, | ||
| 1466 | - attrFlags, message); | ||
| 1467 | - ChannelBuffer txMessage = | ||
| 1468 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1469 | - ctx.getChannel().write(txMessage); | ||
| 1470 | - closeSession(ctx); | ||
| 1471 | - } | ||
| 1472 | - | ||
| 1473 | - /** | ||
| 1474 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1475 | - * Unrecognized Well-known Attribute Error: send NOTIFICATION and close | ||
| 1476 | - * the channel. | ||
| 1477 | - * | ||
| 1478 | - * @param ctx the Channel Handler Context | ||
| 1479 | - * @param attrTypeCode the attribute type code | ||
| 1480 | - * @param attrLen the attribute length (in octets) | ||
| 1481 | - * @param attrFlags the attribute flags | ||
| 1482 | - * @param message the message with the data | ||
| 1483 | - */ | ||
| 1484 | - private void actionsBgpUpdateUnrecognizedWellKnownAttribute( | ||
| 1485 | - ChannelHandlerContext ctx, | ||
| 1486 | - int attrTypeCode, | ||
| 1487 | - int attrLen, | ||
| 1488 | - int attrFlags, | ||
| 1489 | - ChannelBuffer message) { | ||
| 1490 | - log.debug("BGP RX UPDATE Error from {}: " + | ||
| 1491 | - "Unrecognized Well-known Attribute Error: {}", | ||
| 1492 | - remoteAddress, attrTypeCode); | ||
| 1493 | - | ||
| 1494 | - // | ||
| 1495 | - // ERROR: Unrecognized Well-known Attribute | ||
| 1496 | - // | ||
| 1497 | - // Send NOTIFICATION and close the connection | ||
| 1498 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1499 | - int errorSubcode = | ||
| 1500 | - UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE; | ||
| 1501 | - ChannelBuffer data = | ||
| 1502 | - prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen, | ||
| 1503 | - attrFlags, message); | ||
| 1504 | - ChannelBuffer txMessage = | ||
| 1505 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1506 | - ctx.getChannel().write(txMessage); | ||
| 1507 | - closeSession(ctx); | ||
| 1508 | - } | ||
| 1509 | - | ||
| 1510 | - /** | ||
| 1511 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1512 | - * Attribute Length Error: send NOTIFICATION and close the channel. | ||
| 1513 | - * | ||
| 1514 | - * @param ctx the Channel Handler Context | ||
| 1515 | - * @param attrTypeCode the attribute type code | ||
| 1516 | - * @param attrLen the attribute length (in octets) | ||
| 1517 | - * @param attrFlags the attribute flags | ||
| 1518 | - * @param message the message with the data | ||
| 1519 | - */ | ||
| 1520 | - private void actionsBgpUpdateAttributeLengthError( | ||
| 1521 | - ChannelHandlerContext ctx, | ||
| 1522 | - int attrTypeCode, | ||
| 1523 | - int attrLen, | ||
| 1524 | - int attrFlags, | ||
| 1525 | - ChannelBuffer message) { | ||
| 1526 | - log.debug("BGP RX UPDATE Error from {}: Attribute Length Error", | ||
| 1527 | - remoteAddress); | ||
| 1528 | - | ||
| 1529 | - // | ||
| 1530 | - // ERROR: Attribute Length Error | ||
| 1531 | - // | ||
| 1532 | - // Send NOTIFICATION and close the connection | ||
| 1533 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1534 | - int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR; | ||
| 1535 | - ChannelBuffer data = | ||
| 1536 | - prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen, | ||
| 1537 | - attrFlags, message); | ||
| 1538 | - ChannelBuffer txMessage = | ||
| 1539 | - prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1540 | - ctx.getChannel().write(txMessage); | ||
| 1541 | - closeSession(ctx); | ||
| 1542 | - } | ||
| 1543 | - | ||
| 1544 | - /** | ||
| 1545 | - * Applies the appropriate actions after detecting BGP UPDATE | ||
| 1546 | - * Malformed AS_PATH Error: send NOTIFICATION and close the channel. | ||
| 1547 | - * | ||
| 1548 | - * @param ctx the Channel Handler Context | ||
| 1549 | - */ | ||
| 1550 | - private void actionsBgpUpdateMalformedAsPath( | ||
| 1551 | - ChannelHandlerContext ctx) { | ||
| 1552 | - log.debug("BGP RX UPDATE Error from {}: Malformed AS Path", | ||
| 1553 | - remoteAddress); | ||
| 1554 | - | ||
| 1555 | - // | ||
| 1556 | - // ERROR: Malformed AS_PATH | ||
| 1557 | - // | ||
| 1558 | - // Send NOTIFICATION and close the connection | ||
| 1559 | - int errorCode = UpdateMessageError.ERROR_CODE; | ||
| 1560 | - int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH; | ||
| 1561 | - ChannelBuffer txMessage = | ||
| 1562 | - prepareBgpNotification(errorCode, errorSubcode, null); | ||
| 1563 | - ctx.getChannel().write(txMessage); | ||
| 1564 | - closeSession(ctx); | ||
| 1565 | - } | ||
| 1566 | - | ||
| 1567 | - /** | ||
| 1568 | - * Processes BGP NOTIFICATION message. | ||
| 1569 | - * | ||
| 1570 | - * @param ctx the Channel Handler Context | ||
| 1571 | - * @param message the message to process | ||
| 1572 | - */ | ||
| 1573 | - void processBgpNotification(ChannelHandlerContext ctx, | ||
| 1574 | - ChannelBuffer message) { | ||
| 1575 | - int minLength = | ||
| 1576 | - BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | ||
| 1577 | - if (message.readableBytes() < minLength) { | ||
| 1578 | - log.debug("BGP RX NOTIFICATION Error from {}: " + | ||
| 1579 | - "Message length {} too short. Must be at least {}", | ||
| 1580 | - remoteAddress, message.readableBytes(), minLength); | ||
| 1581 | - // | ||
| 1582 | - // ERROR: Bad Message Length | ||
| 1583 | - // | ||
| 1584 | - // NOTE: We do NOT send NOTIFICATION in response to a notification | ||
| 1585 | - return; | ||
| 1586 | - } | ||
| 1587 | - | ||
| 1588 | - // | ||
| 1589 | - // Parse the NOTIFICATION message | ||
| 1590 | - // | ||
| 1591 | - int errorCode = message.readUnsignedByte(); | ||
| 1592 | - int errorSubcode = message.readUnsignedByte(); | ||
| 1593 | - int dataLength = message.readableBytes(); | ||
| 1594 | - | ||
| 1595 | - log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " + | ||
| 1596 | - "Error Subcode {} Data Length {}", | ||
| 1597 | - remoteAddress, errorCode, errorSubcode, dataLength); | ||
| 1598 | - | ||
| 1599 | - // | ||
| 1600 | - // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to | ||
| 1601 | - // close the connection. | ||
| 1602 | - // | ||
| 1603 | - | ||
| 1604 | - // Start the Session Timeout timer | ||
| 1605 | - restartSessionTimeoutTimer(ctx); | ||
| 1606 | - } | ||
| 1607 | - | ||
| 1608 | - /** | ||
| 1609 | - * Processes BGP KEEPALIVE message. | ||
| 1610 | - * | ||
| 1611 | - * @param ctx the Channel Handler Context | ||
| 1612 | - * @param message the message to process | ||
| 1613 | - */ | ||
| 1614 | - void processBgpKeepalive(ChannelHandlerContext ctx, | ||
| 1615 | - ChannelBuffer message) { | ||
| 1616 | - if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH != | ||
| 1617 | - BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) { | ||
| 1618 | - log.debug("BGP RX KEEPALIVE Error from {}: " + | ||
| 1619 | - "Invalid total message length {}. Expected {}", | ||
| 1620 | - remoteAddress, | ||
| 1621 | - message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH, | ||
| 1622 | - BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH); | ||
| 1623 | - // | ||
| 1624 | - // ERROR: Bad Message Length | ||
| 1625 | - // | ||
| 1626 | - // Send NOTIFICATION and close the connection | ||
| 1627 | - ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength( | ||
| 1628 | - message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH); | ||
| 1629 | - ctx.getChannel().write(txMessage); | ||
| 1630 | - closeSession(ctx); | ||
| 1631 | - return; | ||
| 1632 | - } | ||
| 1633 | - | ||
| 1634 | - // | ||
| 1635 | - // Parse the KEEPALIVE message: nothing to do | ||
| 1636 | - // | ||
| 1637 | - log.trace("BGP RX KEEPALIVE message from {}", remoteAddress); | ||
| 1638 | - | ||
| 1639 | - // Start the Session Timeout timer | ||
| 1640 | - restartSessionTimeoutTimer(ctx); | ||
| 1641 | - } | ||
| 1642 | - | ||
| 1643 | - /** | ||
| 1644 | - * Prepares BGP OPEN message. | ||
| 1645 | - * | ||
| 1646 | - * @return the message to transmit (BGP header included) | ||
| 1647 | - */ | ||
| 1648 | - private ChannelBuffer prepareBgpOpen() { | ||
| 1649 | - ChannelBuffer message = | ||
| 1650 | - ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 1651 | - | ||
| 1652 | - // | ||
| 1653 | - // Prepare the OPEN message payload | ||
| 1654 | - // | ||
| 1655 | - message.writeByte(localBgpVersion); | ||
| 1656 | - message.writeShort((int) localAs); | ||
| 1657 | - message.writeShort((int) localHoldtime); | ||
| 1658 | - message.writeInt(bgpSessionManager.getMyBgpId().toInt()); | ||
| 1659 | - message.writeByte(0); // No Optional Parameters | ||
| 1660 | - return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message); | ||
| 1661 | - } | ||
| 1662 | - | ||
| 1663 | - /** | ||
| 1664 | - * Prepares BGP KEEPALIVE message. | ||
| 1665 | - * | ||
| 1666 | - * @return the message to transmit (BGP header included) | ||
| 1667 | - */ | ||
| 1668 | - private ChannelBuffer prepareBgpKeepalive() { | ||
| 1669 | - ChannelBuffer message = | ||
| 1670 | - ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 1671 | - | ||
| 1672 | - // | ||
| 1673 | - // Prepare the KEEPALIVE message payload: nothing to do | ||
| 1674 | - // | ||
| 1675 | - return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message); | ||
| 1676 | - } | ||
| 1677 | - | ||
| 1678 | - /** | ||
| 1679 | - * Prepares BGP NOTIFICATION message. | ||
| 1680 | - * | ||
| 1681 | - * @param errorCode the BGP NOTIFICATION Error Code | ||
| 1682 | - * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable, | ||
| 1683 | - * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC | ||
| 1684 | - * @param data the BGP NOTIFICATION Data if applicable, otherwise null | ||
| 1685 | - * @return the message to transmit (BGP header included) | ||
| 1686 | - */ | ||
| 1687 | - ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode, | ||
| 1688 | - ChannelBuffer data) { | ||
| 1689 | - ChannelBuffer message = | ||
| 1690 | - ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH); | ||
| 1691 | - | ||
| 1692 | - // | ||
| 1693 | - // Prepare the NOTIFICATION message payload | ||
| 1694 | - // | ||
| 1695 | - message.writeByte(errorCode); | ||
| 1696 | - message.writeByte(errorSubcode); | ||
| 1697 | - if (data != null) { | ||
| 1698 | - message.writeBytes(data); | ||
| 1699 | - } | ||
| 1700 | - return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message); | ||
| 1701 | - } | ||
| 1702 | - | ||
| 1703 | - /** | ||
| 1704 | - * Prepares BGP NOTIFICATION message: Bad Message Length. | ||
| 1705 | - * | ||
| 1706 | - * @param length the erroneous Length field | ||
| 1707 | - * @return the message to transmit (BGP header included) | ||
| 1708 | - */ | ||
| 1709 | - ChannelBuffer prepareBgpNotificationBadMessageLength(int length) { | ||
| 1710 | - int errorCode = MessageHeaderError.ERROR_CODE; | ||
| 1711 | - int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH; | ||
| 1712 | - ChannelBuffer data = ChannelBuffers.buffer(2); | ||
| 1713 | - data.writeShort(length); | ||
| 1714 | - | ||
| 1715 | - return prepareBgpNotification(errorCode, errorSubcode, data); | ||
| 1716 | - } | ||
| 1717 | - | ||
| 1718 | - /** | ||
| 1719 | - * Prepares BGP UPDATE Notification data payload. | ||
| 1720 | - * | ||
| 1721 | - * @param attrTypeCode the attribute type code | ||
| 1722 | - * @param attrLen the attribute length (in octets) | ||
| 1723 | - * @param attrFlags the attribute flags | ||
| 1724 | - * @param message the message with the data | ||
| 1725 | - * @return the buffer with the data payload for the BGP UPDATE Notification | ||
| 1726 | - */ | ||
| 1727 | - private ChannelBuffer prepareBgpUpdateNotificationDataPayload( | ||
| 1728 | - int attrTypeCode, | ||
| 1729 | - int attrLen, | ||
| 1730 | - int attrFlags, | ||
| 1731 | - ChannelBuffer message) { | ||
| 1732 | - // Compute the attribute length field octets | ||
| 1733 | - boolean extendedLengthBit = ((0x10 & attrFlags) != 0); | ||
| 1734 | - int attrLenOctets = 1; | ||
| 1735 | - if (extendedLengthBit) { | ||
| 1736 | - attrLenOctets = 2; | ||
| 1737 | - } | ||
| 1738 | - ChannelBuffer data = | ||
| 1739 | - ChannelBuffers.buffer(attrLen + attrLenOctets + 1); | ||
| 1740 | - data.writeByte(attrTypeCode); | ||
| 1741 | - if (extendedLengthBit) { | ||
| 1742 | - data.writeShort(attrLen); | ||
| 1743 | - } else { | ||
| 1744 | - data.writeByte(attrLen); | ||
| 1745 | - } | ||
| 1746 | - data.writeBytes(message, attrLen); | ||
| 1747 | - return data; | ||
| 1748 | - } | ||
| 1749 | - | ||
| 1750 | - /** | ||
| 1751 | - * Prepares BGP message. | ||
| 1752 | - * | ||
| 1753 | - * @param type the BGP message type | ||
| 1754 | - * @param payload the message payload to transmit (BGP header excluded) | ||
| 1755 | - * @return the message to transmit (BGP header included) | ||
| 1756 | - */ | ||
| 1757 | - private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) { | ||
| 1758 | - ChannelBuffer message = | ||
| 1759 | - ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH + | ||
| 1760 | - payload.readableBytes()); | ||
| 1761 | - | ||
| 1762 | - // Write the marker | ||
| 1763 | - for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) { | ||
| 1764 | - message.writeByte(0xff); | ||
| 1765 | - } | ||
| 1766 | - | ||
| 1767 | - // Write the rest of the BGP header | ||
| 1768 | - message.writeShort(BgpConstants.BGP_HEADER_LENGTH + | ||
| 1769 | - payload.readableBytes()); | ||
| 1770 | - message.writeByte(type); | ||
| 1771 | - | ||
| 1772 | - // Write the payload | ||
| 1773 | - message.writeBytes(payload); | ||
| 1774 | - return message; | ||
| 1775 | - } | ||
| 1776 | - | ||
| 1777 | - /** | ||
| 1778 | * Restarts the BGP KeepaliveTimer. | 410 | * Restarts the BGP KeepaliveTimer. |
| 1779 | */ | 411 | */ |
| 1780 | - private void restartKeepaliveTimer(ChannelHandlerContext ctx) { | 412 | + void restartKeepaliveTimer(ChannelHandlerContext ctx) { |
| 1781 | if (localKeepaliveInterval == 0) { | 413 | if (localKeepaliveInterval == 0) { |
| 1782 | return; // Nothing to do | 414 | return; // Nothing to do |
| 1783 | } | 415 | } |
| ... | @@ -1811,7 +443,7 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -1811,7 +443,7 @@ public class BgpSession extends SimpleChannelHandler { |
| 1811 | } | 443 | } |
| 1812 | 444 | ||
| 1813 | // Transmit the KEEPALIVE | 445 | // Transmit the KEEPALIVE |
| 1814 | - ChannelBuffer txMessage = prepareBgpKeepalive(); | 446 | + ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive(); |
| 1815 | ctx.getChannel().write(txMessage); | 447 | ctx.getChannel().write(txMessage); |
| 1816 | 448 | ||
| 1817 | // Restart the KEEPALIVE timer | 449 | // Restart the KEEPALIVE timer |
| ... | @@ -1822,7 +454,7 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -1822,7 +454,7 @@ public class BgpSession extends SimpleChannelHandler { |
| 1822 | /** | 454 | /** |
| 1823 | * Restarts the BGP Session Timeout Timer. | 455 | * Restarts the BGP Session Timeout Timer. |
| 1824 | */ | 456 | */ |
| 1825 | - private void restartSessionTimeoutTimer(ChannelHandlerContext ctx) { | 457 | + void restartSessionTimeoutTimer(ChannelHandlerContext ctx) { |
| 1826 | if (remoteHoldtime == 0) { | 458 | if (remoteHoldtime == 0) { |
| 1827 | return; // Nothing to do | 459 | return; // Nothing to do |
| 1828 | } | 460 | } |
| ... | @@ -1866,30 +498,10 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -1866,30 +498,10 @@ public class BgpSession extends SimpleChannelHandler { |
| 1866 | int errorCode = HoldTimerExpired.ERROR_CODE; | 498 | int errorCode = HoldTimerExpired.ERROR_CODE; |
| 1867 | int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC; | 499 | int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC; |
| 1868 | ChannelBuffer txMessage = | 500 | ChannelBuffer txMessage = |
| 1869 | - prepareBgpNotification(errorCode, errorSubcode, null); | 501 | + BgpNotification.prepareBgpNotification(errorCode, errorSubcode, |
| 502 | + null); | ||
| 1870 | ctx.getChannel().write(txMessage); | 503 | ctx.getChannel().write(txMessage); |
| 1871 | closeChannel(ctx); | 504 | closeChannel(ctx); |
| 1872 | } | 505 | } |
| 1873 | } | 506 | } |
| 1874 | - | ||
| 1875 | - /** | ||
| 1876 | - * An exception indicating a parsing error of the BGP message. | ||
| 1877 | - */ | ||
| 1878 | - private static class BgpParseException extends Exception { | ||
| 1879 | - /** | ||
| 1880 | - * Default constructor. | ||
| 1881 | - */ | ||
| 1882 | - public BgpParseException() { | ||
| 1883 | - super(); | ||
| 1884 | - } | ||
| 1885 | - | ||
| 1886 | - /** | ||
| 1887 | - * Constructor for a specific exception details message. | ||
| 1888 | - * | ||
| 1889 | - * @param message the message with the exception details | ||
| 1890 | - */ | ||
| 1891 | - public BgpParseException(String message) { | ||
| 1892 | - super(message); | ||
| 1893 | - } | ||
| 1894 | - } | ||
| 1895 | } | 507 | } | ... | ... |
| 1 | +/* | ||
| 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 | + */ | ||
| 16 | +package org.onosproject.sdnip.bgp; | ||
| 17 | + | ||
| 18 | +import java.util.ArrayList; | ||
| 19 | +import java.util.Collection; | ||
| 20 | +import java.util.HashMap; | ||
| 21 | +import java.util.Map; | ||
| 22 | + | ||
| 23 | +import org.apache.commons.lang3.tuple.Pair; | ||
| 24 | +import org.jboss.netty.buffer.ChannelBuffer; | ||
| 25 | +import org.jboss.netty.buffer.ChannelBuffers; | ||
| 26 | +import org.jboss.netty.channel.ChannelHandlerContext; | ||
| 27 | +import org.onlab.packet.Ip4Address; | ||
| 28 | +import org.onlab.packet.Ip4Prefix; | ||
| 29 | +import org.onosproject.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError; | ||
| 30 | +import org.slf4j.Logger; | ||
| 31 | +import org.slf4j.LoggerFactory; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * A class for handling BGP UPDATE messages. | ||
| 35 | + */ | ||
| 36 | +final 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 | +} |
| ... | @@ -81,7 +81,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand { | ... | @@ -81,7 +81,7 @@ public class BgpRoutesListCommand extends AbstractShellCommand { |
| 81 | 81 | ||
| 82 | // Print the routes | 82 | // Print the routes |
| 83 | if (foundBgpSession != null) { | 83 | if (foundBgpSession != null) { |
| 84 | - printRoutes(foundBgpSession.getBgpRibIn()); | 84 | + printRoutes(foundBgpSession.bgpRibIn().values()); |
| 85 | } else { | 85 | } else { |
| 86 | printRoutes(service.getBgpRoutes()); | 86 | printRoutes(service.getBgpRoutes()); |
| 87 | } | 87 | } | ... | ... |
| ... | @@ -303,7 +303,7 @@ public class BgpSessionManagerTest { | ... | @@ -303,7 +303,7 @@ public class BgpSessionManagerTest { |
| 303 | private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession, | 303 | private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession, |
| 304 | long expectedRoutes) | 304 | long expectedRoutes) |
| 305 | throws InterruptedException { | 305 | throws InterruptedException { |
| 306 | - Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn(); | 306 | + Collection<BgpRouteEntry> bgpRibIn = bgpSession.bgpRibIn().values(); |
| 307 | 307 | ||
| 308 | final int maxChecks = 500; // Max wait of 5 seconds | 308 | final int maxChecks = 500; // Max wait of 5 seconds |
| 309 | for (int i = 0; i < maxChecks; i++) { | 309 | for (int i = 0; i < maxChecks; i++) { |
| ... | @@ -311,7 +311,7 @@ public class BgpSessionManagerTest { | ... | @@ -311,7 +311,7 @@ public class BgpSessionManagerTest { |
| 311 | break; | 311 | break; |
| 312 | } | 312 | } |
| 313 | Thread.sleep(10); | 313 | Thread.sleep(10); |
| 314 | - bgpRibIn = bgpSession.getBgpRibIn(); | 314 | + bgpRibIn = bgpSession.bgpRibIn().values(); |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | return bgpRibIn; | 317 | return bgpRibIn; | ... | ... |
-
Please register or login to post a comment