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
1892 additions
and
1537 deletions
... | @@ -90,7 +90,8 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -90,7 +90,8 @@ 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 | + errorSubcode, | ||
94 | null); | 95 | null); |
95 | ctx.getChannel().write(txMessage); | 96 | ctx.getChannel().write(txMessage); |
96 | bgpSession.closeSession(ctx); | 97 | bgpSession.closeSession(ctx); |
... | @@ -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,7 +167,7 @@ class BgpFrameDecoder extends FrameDecoder { | ... | @@ -166,7 +167,7 @@ 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); | ... | ... |
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,1536 +407,87 @@ public class BgpSession extends SimpleChannelHandler { | ... | @@ -326,1536 +407,87 @@ public class BgpSession extends SimpleChannelHandler { |
326 | } | 407 | } |
327 | 408 | ||
328 | /** | 409 | /** |
329 | - * Processes BGP OPEN message. | 410 | + * Restarts the BGP KeepaliveTimer. |
330 | - * | ||
331 | - * @param ctx the Channel Handler Context | ||
332 | - * @param message the message to process | ||
333 | */ | 411 | */ |
334 | - void processBgpOpen(ChannelHandlerContext ctx, ChannelBuffer message) { | 412 | + void restartKeepaliveTimer(ChannelHandlerContext ctx) { |
335 | - int minLength = | 413 | + if (localKeepaliveInterval == 0) { |
336 | - BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH; | 414 | + return; // Nothing to do |
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 | } | 415 | } |
447 | - // NOTE: Parse the optional parameters (if needed) | 416 | + keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx), |
448 | - message.readBytes(optParamLen); // NOTE: data ignored | 417 | + localKeepaliveInterval, |
449 | - | 418 | + TimeUnit.SECONDS); |
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 | } | 419 | } |
470 | 420 | ||
471 | - log.debug("BGP RX OPEN message from {}: " + | 421 | + /** |
472 | - "BGPv{} AS {} BGP-ID {} Holdtime {}", | 422 | + * Task class for transmitting KEEPALIVE messages. |
473 | - remoteAddress, remoteBgpVersion, remoteAs, | 423 | + */ |
474 | - remoteBgpId, remoteHoldtime); | 424 | + private final class TransmitKeepaliveTask implements TimerTask { |
475 | - | 425 | + private final ChannelHandlerContext ctx; |
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 | 426 | ||
490 | /** | 427 | /** |
491 | - * Processes BGP UPDATE message. | 428 | + * Constructor for given Channel Handler Context. |
492 | * | 429 | * |
493 | - * @param ctx the Channel Handler Context | 430 | + * @param ctx the Channel Handler Context to use |
494 | - * @param message the message to process | ||
495 | */ | 431 | */ |
496 | - void processBgpUpdate(ChannelHandlerContext ctx, ChannelBuffer message) { | 432 | + TransmitKeepaliveTask(ChannelHandlerContext ctx) { |
497 | - Collection<BgpRouteEntry> addedRoutes = null; | 433 | + this.ctx = ctx; |
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 | } | 434 | } |
516 | 435 | ||
517 | - log.debug("BGP RX UPDATE message from {}", remoteAddress); | 436 | + @Override |
518 | - | 437 | + public void run(Timeout timeout) throws Exception { |
519 | - // | 438 | + if (timeout.isCancelled()) { |
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; | 439 | return; |
531 | } | 440 | } |
532 | - Collection<Ip4Prefix> withdrawnPrefixes = null; | 441 | + if (!ctx.getChannel().isOpen()) { |
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; | 442 | return; |
542 | } | 443 | } |
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 | 444 | ||
552 | - // | 445 | + // Transmit the KEEPALIVE |
553 | - // Parse the Path Attributes | 446 | + ChannelBuffer txMessage = BgpKeepalive.prepareBgpKeepalive(); |
554 | - // | 447 | + ctx.getChannel().write(txMessage); |
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 | 448 | ||
568 | - // Update the BGP RIB-IN | 449 | + // Restart the KEEPALIVE timer |
569 | - for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) { | 450 | + restartKeepaliveTimer(ctx); |
570 | - bgpRibIn.remove(bgpRouteEntry.prefix()); | ||
571 | - } | ||
572 | - for (BgpRouteEntry bgpRouteEntry : addedRoutes) { | ||
573 | - bgpRibIn.put(bgpRouteEntry.prefix(), bgpRouteEntry); | ||
574 | } | 451 | } |
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 | } | 452 | } |
585 | 453 | ||
586 | /** | 454 | /** |
587 | - * Parse BGP Path Attributes from the BGP UPDATE message. | 455 | + * Restarts the BGP Session Timeout Timer. |
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 | */ | 456 | */ |
594 | - private Collection<BgpRouteEntry> parsePathAttributes( | 457 | + void restartSessionTimeoutTimer(ChannelHandlerContext ctx) { |
595 | - ChannelHandlerContext ctx, | 458 | + if (remoteHoldtime == 0) { |
596 | - ChannelBuffer message) | 459 | + return; // Nothing to do |
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 | } | 460 | } |
786 | - addedRoutes.put(prefix, bgpRouteEntry); | 461 | + if (sessionTimeout != null) { |
462 | + sessionTimeout.cancel(); | ||
787 | } | 463 | } |
788 | - | 464 | + sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx), |
789 | - return addedRoutes.values(); | 465 | + remoteHoldtime, |
466 | + TimeUnit.SECONDS); | ||
790 | } | 467 | } |
791 | 468 | ||
792 | /** | 469 | /** |
793 | - * Verifies BGP UPDATE Well-known Attributes. | 470 | + * Task class for BGP Session timeout. |
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 | */ | 471 | */ |
802 | - private void verifyBgpUpdateWellKnownAttributes( | 472 | + private final class SessionTimeoutTask implements TimerTask { |
803 | - ChannelHandlerContext ctx, | 473 | + private final 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 | 474 | ||
843 | /** | 475 | /** |
844 | - * Verifies the BGP UPDATE Attribute Flags. | 476 | + * Constructor for given Channel Handler Context. |
845 | * | 477 | * |
846 | - * @param ctx the Channel Handler Context | 478 | + * @param ctx the Channel Handler Context to use |
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 | */ | 479 | */ |
853 | - private void verifyBgpUpdateAttributeFlags( | 480 | + SessionTimeoutTask(ChannelHandlerContext ctx) { |
854 | - ChannelHandlerContext ctx, | 481 | + this.ctx = 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 | } | 482 | } |
900 | 483 | ||
901 | - // | 484 | + @Override |
902 | - // Verify the Attribute Flags | 485 | + public void run(Timeout timeout) throws Exception { |
903 | - // | 486 | + if (timeout.isCancelled()) { |
904 | - boolean optionalBit = ((0x80 & attrFlags) != 0); | 487 | + return; |
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 | } | 488 | } |
924 | - } | 489 | + if (!ctx.getChannel().isOpen()) { |
925 | - | 490 | + return; |
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. | ||
1779 | - */ | ||
1780 | - private void restartKeepaliveTimer(ChannelHandlerContext ctx) { | ||
1781 | - if (localKeepaliveInterval == 0) { | ||
1782 | - return; // Nothing to do | ||
1783 | - } | ||
1784 | - keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx), | ||
1785 | - localKeepaliveInterval, | ||
1786 | - TimeUnit.SECONDS); | ||
1787 | - } | ||
1788 | - | ||
1789 | - /** | ||
1790 | - * Task class for transmitting KEEPALIVE messages. | ||
1791 | - */ | ||
1792 | - private final class TransmitKeepaliveTask implements TimerTask { | ||
1793 | - private final ChannelHandlerContext ctx; | ||
1794 | - | ||
1795 | - /** | ||
1796 | - * Constructor for given Channel Handler Context. | ||
1797 | - * | ||
1798 | - * @param ctx the Channel Handler Context to use | ||
1799 | - */ | ||
1800 | - TransmitKeepaliveTask(ChannelHandlerContext ctx) { | ||
1801 | - this.ctx = ctx; | ||
1802 | - } | ||
1803 | - | ||
1804 | - @Override | ||
1805 | - public void run(Timeout timeout) throws Exception { | ||
1806 | - if (timeout.isCancelled()) { | ||
1807 | - return; | ||
1808 | - } | ||
1809 | - if (!ctx.getChannel().isOpen()) { | ||
1810 | - return; | ||
1811 | - } | ||
1812 | - | ||
1813 | - // Transmit the KEEPALIVE | ||
1814 | - ChannelBuffer txMessage = prepareBgpKeepalive(); | ||
1815 | - ctx.getChannel().write(txMessage); | ||
1816 | - | ||
1817 | - // Restart the KEEPALIVE timer | ||
1818 | - restartKeepaliveTimer(ctx); | ||
1819 | - } | ||
1820 | - } | ||
1821 | - | ||
1822 | - /** | ||
1823 | - * Restarts the BGP Session Timeout Timer. | ||
1824 | - */ | ||
1825 | - private void restartSessionTimeoutTimer(ChannelHandlerContext ctx) { | ||
1826 | - if (remoteHoldtime == 0) { | ||
1827 | - return; // Nothing to do | ||
1828 | - } | ||
1829 | - if (sessionTimeout != null) { | ||
1830 | - sessionTimeout.cancel(); | ||
1831 | - } | ||
1832 | - sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx), | ||
1833 | - remoteHoldtime, | ||
1834 | - TimeUnit.SECONDS); | ||
1835 | - } | ||
1836 | - | ||
1837 | - /** | ||
1838 | - * Task class for BGP Session timeout. | ||
1839 | - */ | ||
1840 | - private final class SessionTimeoutTask implements TimerTask { | ||
1841 | - private final ChannelHandlerContext ctx; | ||
1842 | - | ||
1843 | - /** | ||
1844 | - * Constructor for given Channel Handler Context. | ||
1845 | - * | ||
1846 | - * @param ctx the Channel Handler Context to use | ||
1847 | - */ | ||
1848 | - SessionTimeoutTask(ChannelHandlerContext ctx) { | ||
1849 | - this.ctx = ctx; | ||
1850 | - } | ||
1851 | - | ||
1852 | - @Override | ||
1853 | - public void run(Timeout timeout) throws Exception { | ||
1854 | - if (timeout.isCancelled()) { | ||
1855 | - return; | ||
1856 | - } | ||
1857 | - if (!ctx.getChannel().isOpen()) { | ||
1858 | - return; | ||
1859 | } | 491 | } |
1860 | 492 | ||
1861 | log.debug("BGP Session Timeout: peer {}", remoteAddress); | 493 | log.debug("BGP Session Timeout: peer {}", remoteAddress); |
... | @@ -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