Toggle navigation
Toggle navigation
This project
Loading...
Sign in
홍길동
/
onos
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
Jonathan Hart
2014-10-16 08:52:55 -0700
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
ab63aacae26e5d232b007c46d73b2d8c2c380beb
ab63aaca
1 parent
bb0272e3
Port the BGP implementation of SDN-IP.
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
3178 additions
and
1 deletions
apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpFrameDecoder.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/package-info.java
utils/misc/src/main/java/org/onlab/packet/IpAddress.java
apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
View file @
ab63aac
...
...
@@ -126,6 +126,8 @@ public class Router implements RouteListener {
bgpIntentsSynchronizerExecutor
=
Executors
.
newSingleThreadExecutor
(
new
ThreadFactoryBuilder
()
.
setNameFormat
(
"bgp-intents-synchronizer-%d"
).
build
());
this
.
hostService
.
addListener
(
new
InternalHostListener
());
}
/**
...
...
apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
View file @
ab63aac
...
...
@@ -10,6 +10,7 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
import
org.onlab.onos.net.host.HostService
;
import
org.onlab.onos.net.intent.IntentService
;
import
org.onlab.onos.sdnip.RouteUpdate.Type
;
import
org.onlab.onos.sdnip.bgp.BgpSessionManager
;
import
org.onlab.onos.sdnip.config.SdnIpConfigReader
;
import
org.onlab.packet.IpAddress
;
import
org.onlab.packet.IpPrefix
;
...
...
@@ -32,6 +33,7 @@ public class SdnIp {
private
SdnIpConfigReader
config
;
private
PeerConnectivity
peerConnectivity
;
private
Router
router
;
private
BgpSessionManager
bgpSessionManager
;
@Activate
protected
void
activate
()
{
...
...
@@ -48,6 +50,9 @@ public class SdnIp {
router
=
new
Router
(
intentService
,
hostService
,
config
,
interfaceService
);
router
.
start
();
bgpSessionManager
=
new
BgpSessionManager
(
router
);
bgpSessionManager
.
startUp
(
2000
);
// TODO
// TODO need to disable link discovery on external ports
router
.
update
(
new
RouteUpdate
(
Type
.
UPDATE
,
new
RouteEntry
(
...
...
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpConstants.java
0 → 100644
View file @
ab63aac
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
/**
* BGP related constants.
*/
public
final
class
BgpConstants
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
BgpConstants
()
{
}
/** BGP port number (RFC 4271). */
public
static
final
int
BGP_PORT
=
179
;
/** BGP version. */
public
static
final
int
BGP_VERSION
=
4
;
/** BGP OPEN message type. */
public
static
final
int
BGP_TYPE_OPEN
=
1
;
/** BGP UPDATE message type. */
public
static
final
int
BGP_TYPE_UPDATE
=
2
;
/** BGP NOTIFICATION message type. */
public
static
final
int
BGP_TYPE_NOTIFICATION
=
3
;
/** BGP KEEPALIVE message type. */
public
static
final
int
BGP_TYPE_KEEPALIVE
=
4
;
/** BGP Header Marker field length. */
public
static
final
int
BGP_HEADER_MARKER_LENGTH
=
16
;
/** BGP Header length. */
public
static
final
int
BGP_HEADER_LENGTH
=
19
;
/** BGP message maximum length. */
public
static
final
int
BGP_MESSAGE_MAX_LENGTH
=
4096
;
/** BGP OPEN message minimum length (BGP Header included). */
public
static
final
int
BGP_OPEN_MIN_LENGTH
=
29
;
/** BGP UPDATE message minimum length (BGP Header included). */
public
static
final
int
BGP_UPDATE_MIN_LENGTH
=
23
;
/** BGP NOTIFICATION message minimum length (BGP Header included). */
public
static
final
int
BGP_NOTIFICATION_MIN_LENGTH
=
21
;
/** BGP KEEPALIVE message expected length (BGP Header included). */
public
static
final
int
BGP_KEEPALIVE_EXPECTED_LENGTH
=
19
;
/** BGP KEEPALIVE messages transmitted per Hold interval. */
public
static
final
int
BGP_KEEPALIVE_PER_HOLD_INTERVAL
=
3
;
/** BGP KEEPALIVE messages minimum Holdtime (in seconds). */
public
static
final
int
BGP_KEEPALIVE_MIN_HOLDTIME
=
3
;
/** BGP KEEPALIVE messages minimum transmission interval (in seconds). */
public
static
final
int
BGP_KEEPALIVE_MIN_INTERVAL
=
1
;
/** BGP AS 0 (zero) value. See draft-ietf-idr-as0-06.txt Internet Draft. */
public
static
final
long
BGP_AS_0
=
0
;
/**
* BGP UPDATE related constants.
*/
public
static
final
class
Update
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
Update
()
{
}
/**
* BGP UPDATE: ORIGIN related constants.
*/
public
static
final
class
Origin
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
Origin
()
{
}
/** BGP UPDATE Attributes Type Code ORIGIN. */
public
static
final
int
TYPE
=
1
;
/** BGP UPDATE Attributes Type Code ORIGIN length. */
public
static
final
int
LENGTH
=
1
;
/** BGP UPDATE ORIGIN: IGP. */
public
static
final
int
IGP
=
0
;
/** BGP UPDATE ORIGIN: EGP. */
public
static
final
int
EGP
=
1
;
/** BGP UPDATE ORIGIN: INCOMPLETE. */
public
static
final
int
INCOMPLETE
=
2
;
}
/**
* BGP UPDATE: AS_PATH related constants.
*/
public
static
final
class
AsPath
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
AsPath
()
{
}
/** BGP UPDATE Attributes Type Code AS_PATH. */
public
static
final
int
TYPE
=
2
;
/** BGP UPDATE AS_PATH Type: AS_SET. */
public
static
final
int
AS_SET
=
1
;
/** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
public
static
final
int
AS_SEQUENCE
=
2
;
}
/**
* BGP UPDATE: NEXT_HOP related constants.
*/
public
static
final
class
NextHop
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
NextHop
()
{
}
/** BGP UPDATE Attributes Type Code NEXT_HOP. */
public
static
final
int
TYPE
=
3
;
/** BGP UPDATE Attributes Type Code NEXT_HOP length. */
public
static
final
int
LENGTH
=
4
;
}
/**
* BGP UPDATE: MULTI_EXIT_DISC related constants.
*/
public
static
final
class
MultiExitDisc
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
MultiExitDisc
()
{
}
/** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC. */
public
static
final
int
TYPE
=
4
;
/** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC length. */
public
static
final
int
LENGTH
=
4
;
/** BGP UPDATE Attributes lowest MULTI_EXIT_DISC value. */
public
static
final
int
LOWEST_MULTI_EXIT_DISC
=
0
;
}
/**
* BGP UPDATE: LOCAL_PREF related constants.
*/
public
static
final
class
LocalPref
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
LocalPref
()
{
}
/** BGP UPDATE Attributes Type Code LOCAL_PREF. */
public
static
final
int
TYPE
=
5
;
/** BGP UPDATE Attributes Type Code LOCAL_PREF length. */
public
static
final
int
LENGTH
=
4
;
}
/**
* BGP UPDATE: ATOMIC_AGGREGATE related constants.
*/
public
static
final
class
AtomicAggregate
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
AtomicAggregate
()
{
}
/** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE. */
public
static
final
int
TYPE
=
6
;
/** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE length. */
public
static
final
int
LENGTH
=
0
;
}
/**
* BGP UPDATE: AGGREGATOR related constants.
*/
public
static
final
class
Aggregator
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
Aggregator
()
{
}
/** BGP UPDATE Attributes Type Code AGGREGATOR. */
public
static
final
int
TYPE
=
7
;
/** BGP UPDATE Attributes Type Code AGGREGATOR length. */
public
static
final
int
LENGTH
=
6
;
}
}
/**
* BGP NOTIFICATION related constants.
*/
public
static
final
class
Notifications
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
Notifications
()
{
}
/**
* BGP NOTIFICATION: Message Header Error constants.
*/
public
static
final
class
MessageHeaderError
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
MessageHeaderError
()
{
}
/** Message Header Error code. */
public
static
final
int
ERROR_CODE
=
1
;
/** Message Header Error subcode: Connection Not Synchronized. */
public
static
final
int
CONNECTION_NOT_SYNCHRONIZED
=
1
;
/** Message Header Error subcode: Bad Message Length. */
public
static
final
int
BAD_MESSAGE_LENGTH
=
2
;
/** Message Header Error subcode: Bad Message Type. */
public
static
final
int
BAD_MESSAGE_TYPE
=
3
;
}
/**
* BGP NOTIFICATION: OPEN Message Error constants.
*/
public
static
final
class
OpenMessageError
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
OpenMessageError
()
{
}
/** OPEN Message Error code. */
public
static
final
int
ERROR_CODE
=
2
;
/** OPEN Message Error subcode: Unsupported Version Number. */
public
static
final
int
UNSUPPORTED_VERSION_NUMBER
=
1
;
/** OPEN Message Error subcode: Bad PEER AS. */
public
static
final
int
BAD_PEER_AS
=
2
;
/** OPEN Message Error subcode: Unacceptable Hold Time. */
public
static
final
int
UNACCEPTABLE_HOLD_TIME
=
6
;
}
/**
* BGP NOTIFICATION: UPDATE Message Error constants.
*/
public
static
final
class
UpdateMessageError
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
UpdateMessageError
()
{
}
/** UPDATE Message Error code. */
public
static
final
int
ERROR_CODE
=
3
;
/** UPDATE Message Error subcode: Malformed Attribute List. */
public
static
final
int
MALFORMED_ATTRIBUTE_LIST
=
1
;
/** UPDATE Message Error subcode: Unrecognized Well-known Attribute. */
public
static
final
int
UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
=
2
;
/** UPDATE Message Error subcode: Missing Well-known Attribute. */
public
static
final
int
MISSING_WELL_KNOWN_ATTRIBUTE
=
3
;
/** UPDATE Message Error subcode: Attribute Flags Error. */
public
static
final
int
ATTRIBUTE_FLAGS_ERROR
=
4
;
/** UPDATE Message Error subcode: Attribute Length Error. */
public
static
final
int
ATTRIBUTE_LENGTH_ERROR
=
5
;
/** UPDATE Message Error subcode: Invalid ORIGIN Attribute. */
public
static
final
int
INVALID_ORIGIN_ATTRIBUTE
=
6
;
/** UPDATE Message Error subcode: Invalid NEXT_HOP Attribute. */
public
static
final
int
INVALID_NEXT_HOP_ATTRIBUTE
=
8
;
/** UPDATE Message Error subcode: Optional Attribute Error. Unused. */
public
static
final
int
OPTIONAL_ATTRIBUTE_ERROR
=
9
;
/** UPDATE Message Error subcode: Invalid Network Field. */
public
static
final
int
INVALID_NETWORK_FIELD
=
10
;
/** UPDATE Message Error subcode: Malformed AS_PATH. */
public
static
final
int
MALFORMED_AS_PATH
=
11
;
}
/**
* BGP NOTIFICATION: Hold Timer Expired constants.
*/
public
static
final
class
HoldTimerExpired
{
/**
* Default constructor.
* <p>
* The constructor is private to prevent creating an instance of
* this utility class.
*/
private
HoldTimerExpired
()
{
}
/** Hold Timer Expired code. */
public
static
final
int
ERROR_CODE
=
4
;
}
/** BGP NOTIFICATION message Error subcode: Unspecific. */
public
static
final
int
ERROR_SUBCODE_UNSPECIFIC
=
0
;
}
}
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpFrameDecoder.java
0 → 100644
View file @
ab63aac
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
import
org.jboss.netty.buffer.ChannelBuffer
;
import
org.jboss.netty.buffer.ChannelBuffers
;
import
org.jboss.netty.channel.Channel
;
import
org.jboss.netty.channel.ChannelHandlerContext
;
import
org.jboss.netty.handler.codec.frame.FrameDecoder
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* Class for handling the decoding of the BGP messages.
*/
class
BgpFrameDecoder
extends
FrameDecoder
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
BgpFrameDecoder
.
class
);
private
final
BgpSession
bgpSession
;
/**
* Constructor for a given BGP Session.
*
* @param bgpSession the BGP session state to use.
*/
BgpFrameDecoder
(
BgpSession
bgpSession
)
{
this
.
bgpSession
=
bgpSession
;
}
@Override
protected
Object
decode
(
ChannelHandlerContext
ctx
,
Channel
channel
,
ChannelBuffer
buf
)
throws
Exception
{
//
// NOTE: If we close the channel during the decoding, we might still
// see some incoming messages while the channel closing is completed.
//
if
(
bgpSession
.
isClosed
())
{
return
null
;
}
log
.
trace
(
"BGP Peer: decode(): remoteAddr = {} localAddr = {} "
+
"messageSize = {}"
,
ctx
.
getChannel
().
getRemoteAddress
(),
ctx
.
getChannel
().
getLocalAddress
(),
buf
.
readableBytes
());
// Test for minimum length of the BGP message
if
(
buf
.
readableBytes
()
<
BgpConstants
.
BGP_HEADER_LENGTH
)
{
// No enough data received
return
null
;
}
//
// Mark the current buffer position in case we haven't received
// the whole message.
//
buf
.
markReaderIndex
();
//
// Read and check the BGP message Marker field: it must be all ones
// (See RFC 4271, Section 4.1)
//
byte
[]
marker
=
new
byte
[
BgpConstants
.
BGP_HEADER_MARKER_LENGTH
];
buf
.
readBytes
(
marker
);
for
(
int
i
=
0
;
i
<
marker
.
length
;
i
++)
{
if
(
marker
[
i
]
!=
(
byte
)
0xff
)
{
log
.
debug
(
"BGP RX Error: invalid marker {} at position {}"
,
marker
[
i
],
i
);
//
// ERROR: Connection Not Synchronized
//
// Send NOTIFICATION and close the connection
int
errorCode
=
MessageHeaderError
.
ERROR_CODE
;
int
errorSubcode
=
MessageHeaderError
.
CONNECTION_NOT_SYNCHRONIZED
;
ChannelBuffer
txMessage
=
bgpSession
.
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
bgpSession
.
closeChannel
(
ctx
);
return
null
;
}
}
//
// Read and check the BGP message Length field
//
int
length
=
buf
.
readUnsignedShort
();
if
((
length
<
BgpConstants
.
BGP_HEADER_LENGTH
)
||
(
length
>
BgpConstants
.
BGP_MESSAGE_MAX_LENGTH
))
{
log
.
debug
(
"BGP RX Error: invalid Length field {}. "
+
"Must be between {} and {}"
,
length
,
BgpConstants
.
BGP_HEADER_LENGTH
,
BgpConstants
.
BGP_MESSAGE_MAX_LENGTH
);
//
// ERROR: Bad Message Length
//
// Send NOTIFICATION and close the connection
ChannelBuffer
txMessage
=
bgpSession
.
prepareBgpNotificationBadMessageLength
(
length
);
ctx
.
getChannel
().
write
(
txMessage
);
bgpSession
.
closeChannel
(
ctx
);
return
null
;
}
//
// Test whether the rest of the message is received:
// So far we have read the Marker (16 octets) and the
// Length (2 octets) fields.
//
int
remainingMessageLen
=
length
-
BgpConstants
.
BGP_HEADER_MARKER_LENGTH
-
2
;
if
(
buf
.
readableBytes
()
<
remainingMessageLen
)
{
// No enough data received
buf
.
resetReaderIndex
();
return
null
;
}
//
// Read the BGP message Type field, and process based on that type
//
int
type
=
buf
.
readUnsignedByte
();
remainingMessageLen
--;
// Adjust after reading the type
ChannelBuffer
message
=
buf
.
readBytes
(
remainingMessageLen
);
//
// Process the remaining of the message based on the message type
//
switch
(
type
)
{
case
BgpConstants
.
BGP_TYPE_OPEN
:
bgpSession
.
processBgpOpen
(
ctx
,
message
);
break
;
case
BgpConstants
.
BGP_TYPE_UPDATE
:
bgpSession
.
processBgpUpdate
(
ctx
,
message
);
break
;
case
BgpConstants
.
BGP_TYPE_NOTIFICATION
:
bgpSession
.
processBgpNotification
(
ctx
,
message
);
break
;
case
BgpConstants
.
BGP_TYPE_KEEPALIVE
:
bgpSession
.
processBgpKeepalive
(
ctx
,
message
);
break
;
default
:
//
// ERROR: Bad Message Type
//
// Send NOTIFICATION and close the connection
int
errorCode
=
MessageHeaderError
.
ERROR_CODE
;
int
errorSubcode
=
MessageHeaderError
.
BAD_MESSAGE_TYPE
;
ChannelBuffer
data
=
ChannelBuffers
.
buffer
(
1
);
data
.
writeByte
(
type
);
ChannelBuffer
txMessage
=
bgpSession
.
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
bgpSession
.
closeChannel
(
ctx
);
return
null
;
}
return
null
;
}
}
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpRouteEntry.java
0 → 100644
View file @
ab63aac
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
import
static
com
.
google
.
common
.
base
.
Preconditions
.
checkNotNull
;
import
java.util.ArrayList
;
import
java.util.Objects
;
import
org.onlab.onos.sdnip.RouteEntry
;
import
org.onlab.packet.IpAddress
;
import
org.onlab.packet.IpPrefix
;
import
com.google.common.base.MoreObjects
;
/**
* Represents a route in BGP.
*/
public
class
BgpRouteEntry
extends
RouteEntry
{
private
final
BgpSession
bgpSession
;
// The BGP Session the route was
// received on
private
final
byte
origin
;
// Route ORIGIN: IGP, EGP, INCOMPLETE
private
final
AsPath
asPath
;
// The AS Path
private
final
long
localPref
;
// The local preference for the route
private
long
multiExitDisc
=
BgpConstants
.
Update
.
MultiExitDisc
.
LOWEST_MULTI_EXIT_DISC
;
/**
* Class constructor.
*
* @param bgpSession the BGP Session the route was received on
* @param prefix the prefix of the route
* @param nextHop the next hop of the route
* @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
* @param asPath the AS path
* @param localPref the route local preference
*/
public
BgpRouteEntry
(
BgpSession
bgpSession
,
IpPrefix
prefix
,
IpAddress
nextHop
,
byte
origin
,
BgpRouteEntry
.
AsPath
asPath
,
long
localPref
)
{
super
(
prefix
,
nextHop
);
this
.
bgpSession
=
checkNotNull
(
bgpSession
);
this
.
origin
=
origin
;
this
.
asPath
=
checkNotNull
(
asPath
);
this
.
localPref
=
localPref
;
}
/**
* Gets the BGP Session the route was received on.
*
* @return the BGP Session the route was received on
*/
public
BgpSession
getBgpSession
()
{
return
bgpSession
;
}
/**
* Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
*
* @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
*/
public
byte
getOrigin
()
{
return
origin
;
}
/**
* Gets the route AS path.
*
* @return the route AS path
*/
public
BgpRouteEntry
.
AsPath
getAsPath
()
{
return
asPath
;
}
/**
* Gets the route local preference.
*
* @return the route local preference
*/
public
long
getLocalPref
()
{
return
localPref
;
}
/**
* Gets the route MED (Multi-Exit Discriminator).
*
* @return the route MED (Multi-Exit Discriminator)
*/
public
long
getMultiExitDisc
()
{
return
multiExitDisc
;
}
/**
* Sets the route MED (Multi-Exit Discriminator).
*
* @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
*/
void
setMultiExitDisc
(
long
multiExitDisc
)
{
this
.
multiExitDisc
=
multiExitDisc
;
}
/**
* Tests whether the route is originated from the local AS.
* <p/>
* The route is considered originated from the local AS if the AS Path
* is empty or if it begins with an AS_SET.
*
* @return true if the route is originated from the local AS, otherwise
* false
*/
boolean
isLocalRoute
()
{
if
(
asPath
.
getPathSegments
().
isEmpty
())
{
return
true
;
}
PathSegment
firstPathSegment
=
asPath
.
getPathSegments
().
get
(
0
);
if
(
firstPathSegment
.
getType
()
==
BgpConstants
.
Update
.
AsPath
.
AS_SET
)
{
return
true
;
}
return
false
;
}
/**
* Gets the BGP Neighbor AS number the route was received from.
* <p/>
* If the router is originated from the local AS, the return value is
* zero (BGP_AS_0).
*
* @return the BGP Neighbor AS number the route was received from.
*/
long
getNeighborAs
()
{
if
(
isLocalRoute
())
{
return
BgpConstants
.
BGP_AS_0
;
}
PathSegment
firstPathSegment
=
asPath
.
getPathSegments
().
get
(
0
);
if
(
firstPathSegment
.
getSegmentAsNumbers
().
isEmpty
())
{
// TODO: Shouldn't happen. Should check during the parsing.
return
BgpConstants
.
BGP_AS_0
;
}
return
firstPathSegment
.
getSegmentAsNumbers
().
get
(
0
);
}
/**
* Tests whether the AS Path contains a loop.
* <p/>
* The test is done by comparing whether the AS Path contains the
* local AS number.
*
* @param localAsNumber the local AS number to compare against
* @return true if the AS Path contains a loop, otherwise false
*/
boolean
hasAsPathLoop
(
long
localAsNumber
)
{
for
(
PathSegment
pathSegment
:
asPath
.
getPathSegments
())
{
for
(
Long
asNumber
:
pathSegment
.
getSegmentAsNumbers
())
{
if
(
asNumber
.
equals
(
localAsNumber
))
{
return
true
;
}
}
}
return
false
;
}
/**
* Compares this BGP route against another BGP route by using the
* BGP Decision Process.
* <p/>
* NOTE: The comparison needs to be performed only on routes that have
* same IP Prefix.
*
* @param other the BGP route to compare against
* @return true if this BGP route is better than the other BGP route
* or same, otherwise false
*/
boolean
isBetterThan
(
BgpRouteEntry
other
)
{
if
(
this
==
other
)
{
return
true
;
// Return true if same route
}
// Compare the LOCAL_PREF values: larger is better
if
(
getLocalPref
()
!=
other
.
getLocalPref
())
{
return
(
getLocalPref
()
>
other
.
getLocalPref
());
}
// Compare the AS number in the path: smaller is better
if
(
getAsPath
().
getAsPathLength
()
!=
other
.
getAsPath
().
getAsPathLength
())
{
return
getAsPath
().
getAsPathLength
()
<
other
.
getAsPath
().
getAsPathLength
();
}
// Compare the Origin number: lower is better
if
(
getOrigin
()
!=
other
.
getOrigin
())
{
return
(
getOrigin
()
<
other
.
getOrigin
());
}
// Compare the MED if the neighbor AS is same: larger is better
medLabel:
{
boolean
thisIsLocalRoute
=
isLocalRoute
();
if
(
thisIsLocalRoute
!=
other
.
isLocalRoute
())
{
break
medLabel
;
// AS number is different
}
if
(!
thisIsLocalRoute
)
{
long
thisNeighborAs
=
getNeighborAs
();
if
(
thisNeighborAs
!=
other
.
getNeighborAs
())
{
break
medLabel
;
// AS number is different
}
if
(
thisNeighborAs
==
BgpConstants
.
BGP_AS_0
)
{
break
medLabel
;
// Invalid AS number
}
}
// Compare the MED
if
(
getMultiExitDisc
()
!=
other
.
getMultiExitDisc
())
{
return
(
getMultiExitDisc
()
>
other
.
getMultiExitDisc
());
}
}
// Compare the peer BGP ID: lower is better
IpAddress
peerBgpId
=
getBgpSession
().
getRemoteBgpId
();
IpAddress
otherPeerBgpId
=
other
.
getBgpSession
().
getRemoteBgpId
();
if
(!
peerBgpId
.
equals
(
otherPeerBgpId
))
{
return
(
peerBgpId
.
compareTo
(
otherPeerBgpId
)
<
0
);
}
// Compare the peer BGP address: lower is better
IpAddress
peerAddress
=
getBgpSession
().
getRemoteIp4Address
();
IpAddress
otherPeerAddress
=
other
.
getBgpSession
().
getRemoteIp4Address
();
if
(!
peerAddress
.
equals
(
otherPeerAddress
))
{
return
(
peerAddress
.
compareTo
(
otherPeerAddress
)
<
0
);
}
return
true
;
// Routes are same. Shouldn't happen?
}
/**
* A class to represent AS Path Segment.
*/
public
static
class
PathSegment
{
private
final
byte
type
;
// Segment type: AS_SET, AS_SEQUENCE
private
final
ArrayList
<
Long
>
segmentAsNumbers
;
// Segment AS numbers
/**
* Constructor.
*
* @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
* @param segmentAsNumbers the Segment AS numbers
*/
PathSegment
(
byte
type
,
ArrayList
<
Long
>
segmentAsNumbers
)
{
this
.
type
=
type
;
this
.
segmentAsNumbers
=
checkNotNull
(
segmentAsNumbers
);
}
/**
* Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
*
* @return the Path Segment Type: AS_SET, AS_SEQUENCE
*/
public
byte
getType
()
{
return
type
;
}
/**
* Gets the Path Segment AS Numbers.
*
* @return the Path Segment AS Numbers
*/
public
ArrayList
<
Long
>
getSegmentAsNumbers
()
{
return
segmentAsNumbers
;
}
@Override
public
boolean
equals
(
Object
other
)
{
if
(
this
==
other
)
{
return
true
;
}
if
(!(
other
instanceof
PathSegment
))
{
return
false
;
}
PathSegment
otherPathSegment
=
(
PathSegment
)
other
;
return
Objects
.
equals
(
this
.
type
,
otherPathSegment
.
type
)
&&
Objects
.
equals
(
this
.
segmentAsNumbers
,
otherPathSegment
.
segmentAsNumbers
);
}
@Override
public
int
hashCode
()
{
return
Objects
.
hash
(
type
,
segmentAsNumbers
);
}
@Override
public
String
toString
()
{
return
MoreObjects
.
toStringHelper
(
getClass
())
.
add
(
"type"
,
this
.
type
)
.
add
(
"segmentAsNumbers"
,
this
.
segmentAsNumbers
)
.
toString
();
}
}
/**
* A class to represent AS Path.
*/
public
static
class
AsPath
{
private
final
ArrayList
<
PathSegment
>
pathSegments
;
private
final
int
asPathLength
;
// Precomputed AS Path Length
/**
* Constructor.
*
* @param pathSegments the Path Segments of the Path
*/
AsPath
(
ArrayList
<
PathSegment
>
pathSegments
)
{
this
.
pathSegments
=
checkNotNull
(
pathSegments
);
//
// Precompute the AS Path Length:
// - AS_SET counts as 1
//
int
pl
=
0
;
for
(
PathSegment
pathSegment
:
pathSegments
)
{
if
(
pathSegment
.
getType
()
==
BgpConstants
.
Update
.
AsPath
.
AS_SET
)
{
pl
++;
continue
;
}
pl
+=
pathSegment
.
getSegmentAsNumbers
().
size
();
}
asPathLength
=
pl
;
}
/**
* Gets the AS Path Segments.
*
* @return the AS Path Segments
*/
public
ArrayList
<
PathSegment
>
getPathSegments
()
{
return
pathSegments
;
}
/**
* Gets the AS Path Length as considered by the BGP Decision Process.
*
* @return the AS Path Length as considered by the BGP Decision Process
*/
int
getAsPathLength
()
{
return
asPathLength
;
}
@Override
public
boolean
equals
(
Object
other
)
{
if
(
this
==
other
)
{
return
true
;
}
if
(!(
other
instanceof
AsPath
))
{
return
false
;
}
AsPath
otherAsPath
=
(
AsPath
)
other
;
return
Objects
.
equals
(
this
.
pathSegments
,
otherAsPath
.
pathSegments
);
}
@Override
public
int
hashCode
()
{
return
Objects
.
hash
(
pathSegments
);
}
@Override
public
String
toString
()
{
return
MoreObjects
.
toStringHelper
(
getClass
())
.
add
(
"pathSegments"
,
this
.
pathSegments
)
.
toString
();
}
}
/**
* Compares whether two objects are equal.
* <p/>
* NOTE: The bgpSession field is excluded from the comparison.
*
* @return true if the two objects are equal, otherwise false.
*/
@Override
public
boolean
equals
(
Object
other
)
{
if
(
this
==
other
)
{
return
true
;
}
//
// NOTE: Subclasses are considered as change of identity, hence
// equals() will return false if the class type doesn't match.
//
if
(
other
==
null
||
getClass
()
!=
other
.
getClass
())
{
return
false
;
}
if
(!
super
.
equals
(
other
))
{
return
false
;
}
// NOTE: The bgpSession field is excluded from the comparison
BgpRouteEntry
otherRoute
=
(
BgpRouteEntry
)
other
;
return
(
this
.
origin
==
otherRoute
.
origin
)
&&
Objects
.
equals
(
this
.
asPath
,
otherRoute
.
asPath
)
&&
(
this
.
localPref
==
otherRoute
.
localPref
)
&&
(
this
.
multiExitDisc
==
otherRoute
.
multiExitDisc
);
}
/**
* Computes the hash code.
* <p/>
* NOTE: We return the base class hash code to avoid expensive computation
*
* @return the object hash code
*/
@Override
public
int
hashCode
()
{
return
super
.
hashCode
();
}
@Override
public
String
toString
()
{
return
MoreObjects
.
toStringHelper
(
getClass
())
.
add
(
"prefix"
,
prefix
())
.
add
(
"nextHop"
,
nextHop
())
.
add
(
"bgpId"
,
bgpSession
.
getRemoteBgpId
())
.
add
(
"origin"
,
origin
)
.
add
(
"asPath"
,
asPath
)
.
add
(
"localPref"
,
localPref
)
.
add
(
"multiExitDisc"
,
multiExitDisc
)
.
toString
();
}
}
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSession.java
0 → 100644
View file @
ab63aac
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
import
java.net.InetAddress
;
import
java.net.InetSocketAddress
;
import
java.net.SocketAddress
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentMap
;
import
java.util.concurrent.TimeUnit
;
import
org.apache.commons.lang3.tuple.Pair
;
import
org.jboss.netty.buffer.ChannelBuffer
;
import
org.jboss.netty.buffer.ChannelBuffers
;
import
org.jboss.netty.channel.ChannelHandlerContext
;
import
org.jboss.netty.channel.ChannelStateEvent
;
import
org.jboss.netty.channel.SimpleChannelHandler
;
import
org.jboss.netty.util.HashedWheelTimer
;
import
org.jboss.netty.util.Timeout
;
import
org.jboss.netty.util.Timer
;
import
org.jboss.netty.util.TimerTask
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.OpenMessageError
;
import
org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError
;
import
org.onlab.packet.IpAddress
;
import
org.onlab.packet.IpPrefix
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* Class for handling the BGP peer sessions.
* There is one instance per each BGP peer session.
*/
public
class
BgpSession
extends
SimpleChannelHandler
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
BgpSession
.
class
);
private
final
BgpSessionManager
bgpSessionManager
;
// Local flag to indicate the session is closed.
// It is used to avoid the Netty's asynchronous closing of a channel.
private
boolean
isClosed
=
false
;
private
SocketAddress
remoteAddress
;
// Peer IP addr/port
private
IpAddress
remoteIp4Address
;
// Peer IPv4 address
private
int
remoteBgpVersion
;
// 1 octet
private
long
remoteAs
;
// 2 octets
private
long
remoteHoldtime
;
// 2 octets
private
IpAddress
remoteBgpId
;
// 4 octets -> IPv4 address
//
private
SocketAddress
localAddress
;
// Local IP addr/port
private
IpAddress
localIp4Address
;
// Local IPv4 address
private
int
localBgpVersion
;
// 1 octet
private
long
localAs
;
// 2 octets
private
long
localHoldtime
;
// 2 octets
private
IpAddress
localBgpId
;
// 4 octets -> IPv4 address
//
private
long
localKeepaliveInterval
;
// Keepalive interval
// Timers state
private
Timer
timer
=
new
HashedWheelTimer
();
private
volatile
Timeout
keepaliveTimeout
;
// Periodic KEEPALIVE
private
volatile
Timeout
sessionTimeout
;
// Session timeout
// BGP RIB-IN routing entries from this peer
private
ConcurrentMap
<
IpPrefix
,
BgpRouteEntry
>
bgpRibIn
=
new
ConcurrentHashMap
<>();
/**
* Constructor for a given BGP Session Manager.
*
* @param bgpSessionManager the BGP Session Manager to use
*/
BgpSession
(
BgpSessionManager
bgpSessionManager
)
{
this
.
bgpSessionManager
=
bgpSessionManager
;
}
/**
* Gets the BGP RIB-IN routing entries.
*
* @return the BGP RIB-IN routing entries
*/
public
Collection
<
BgpRouteEntry
>
getBgpRibIn
()
{
return
bgpRibIn
.
values
();
}
/**
* Finds a BGP routing entry in the BGP RIB-IN.
*
* @param prefix the prefix of the route to search for
* @return the BGP routing entry if found, otherwise null
*/
public
BgpRouteEntry
findBgpRouteEntry
(
IpPrefix
prefix
)
{
return
bgpRibIn
.
get
(
prefix
);
}
/**
* Gets the BGP session remote address.
*
* @return the BGP session remote address
*/
public
SocketAddress
getRemoteAddress
()
{
return
remoteAddress
;
}
/**
* Gets the BGP session remote IPv4 address.
*
* @return the BGP session remote IPv4 address
*/
public
IpAddress
getRemoteIp4Address
()
{
return
remoteIp4Address
;
}
/**
* Gets the BGP session remote BGP version.
*
* @return the BGP session remote BGP version
*/
public
int
getRemoteBgpVersion
()
{
return
remoteBgpVersion
;
}
/**
* Gets the BGP session remote AS number.
*
* @return the BGP session remote AS number
*/
public
long
getRemoteAs
()
{
return
remoteAs
;
}
/**
* Gets the BGP session remote Holdtime.
*
* @return the BGP session remote Holdtime
*/
public
long
getRemoteHoldtime
()
{
return
remoteHoldtime
;
}
/**
* Gets the BGP session remote BGP Identifier as an IPv4 address.
*
* @return the BGP session remote BGP Identifier as an IPv4 address
*/
public
IpAddress
getRemoteBgpId
()
{
return
remoteBgpId
;
}
/**
* Gets the BGP session local address.
*
* @return the BGP session local address
*/
public
SocketAddress
getLocalAddress
()
{
return
localAddress
;
}
/**
* Gets the BGP session local BGP version.
*
* @return the BGP session local BGP version
*/
public
int
getLocalBgpVersion
()
{
return
localBgpVersion
;
}
/**
* Gets the BGP session local AS number.
*
* @return the BGP session local AS number
*/
public
long
getLocalAs
()
{
return
localAs
;
}
/**
* Gets the BGP session local Holdtime.
*
* @return the BGP session local Holdtime
*/
public
long
getLocalHoldtime
()
{
return
localHoldtime
;
}
/**
* Gets the BGP session local BGP Identifier as an IPv4 address.
*
* @return the BGP session local BGP Identifier as an IPv4 address
*/
public
IpAddress
getLocalBgpId
()
{
return
localBgpId
;
}
/**
* Tests whether the session is closed.
* <p/>
* NOTE: We use this method to avoid the Netty's asynchronous closing
* of a channel.
*
* @param return true if the session is closed
*/
boolean
isClosed
()
{
return
isClosed
;
}
/**
* Closes the channel.
*
* @param ctx the Channel Handler Context
*/
void
closeChannel
(
ChannelHandlerContext
ctx
)
{
isClosed
=
true
;
timer
.
stop
();
ctx
.
getChannel
().
close
();
}
@Override
public
void
channelConnected
(
ChannelHandlerContext
ctx
,
ChannelStateEvent
channelEvent
)
{
localAddress
=
ctx
.
getChannel
().
getLocalAddress
();
remoteAddress
=
ctx
.
getChannel
().
getRemoteAddress
();
// Assign the local and remote IPv4 addresses
InetAddress
inetAddr
;
if
(
localAddress
instanceof
InetSocketAddress
)
{
inetAddr
=
((
InetSocketAddress
)
localAddress
).
getAddress
();
localIp4Address
=
IpAddress
.
valueOf
(
inetAddr
.
getAddress
());
}
if
(
remoteAddress
instanceof
InetSocketAddress
)
{
inetAddr
=
((
InetSocketAddress
)
remoteAddress
).
getAddress
();
remoteIp4Address
=
IpAddress
.
valueOf
(
inetAddr
.
getAddress
());
}
log
.
debug
(
"BGP Session Connected from {} on {}"
,
remoteAddress
,
localAddress
);
if
(!
bgpSessionManager
.
peerConnected
(
this
))
{
log
.
debug
(
"Cannot setup BGP Session Connection from {}. Closing..."
,
remoteAddress
);
ctx
.
getChannel
().
close
();
}
}
@Override
public
void
channelDisconnected
(
ChannelHandlerContext
ctx
,
ChannelStateEvent
channelEvent
)
{
log
.
debug
(
"BGP Session Disconnected from {} on {}"
,
ctx
.
getChannel
().
getRemoteAddress
(),
ctx
.
getChannel
().
getLocalAddress
());
//
// Withdraw the routes advertised by this BGP peer
//
// NOTE: We must initialize the RIB-IN before propagating the withdraws
// for further processing. Otherwise, the BGP Decision Process
// will use those routes again.
//
Collection
<
BgpRouteEntry
>
deletedRoutes
=
bgpRibIn
.
values
();
bgpRibIn
=
new
ConcurrentHashMap
<>();
// Push the updates to the BGP Merged RIB
BgpSessionManager
.
BgpRouteSelector
bgpRouteSelector
=
bgpSessionManager
.
getBgpRouteSelector
();
Collection
<
BgpRouteEntry
>
addedRoutes
=
Collections
.
emptyList
();
bgpRouteSelector
.
routeUpdates
(
this
,
addedRoutes
,
deletedRoutes
);
bgpSessionManager
.
peerDisconnected
(
this
);
}
/**
* Processes BGP OPEN message.
*
* @param ctx the Channel Handler Context
* @param message the message to process
*/
void
processBgpOpen
(
ChannelHandlerContext
ctx
,
ChannelBuffer
message
)
{
int
minLength
=
BgpConstants
.
BGP_OPEN_MIN_LENGTH
-
BgpConstants
.
BGP_HEADER_LENGTH
;
if
(
message
.
readableBytes
()
<
minLength
)
{
log
.
debug
(
"BGP RX OPEN Error from {}: "
+
"Message length {} too short. Must be at least {}"
,
remoteAddress
,
message
.
readableBytes
(),
minLength
);
//
// ERROR: Bad Message Length
//
// Send NOTIFICATION and close the connection
ChannelBuffer
txMessage
=
prepareBgpNotificationBadMessageLength
(
message
.
readableBytes
()
+
BgpConstants
.
BGP_HEADER_LENGTH
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
//
// Parse the OPEN message
//
// Remote BGP version
remoteBgpVersion
=
message
.
readUnsignedByte
();
if
(
remoteBgpVersion
!=
BgpConstants
.
BGP_VERSION
)
{
log
.
debug
(
"BGP RX OPEN Error from {}: "
+
"Unsupported BGP version {}. Should be {}"
,
remoteAddress
,
remoteBgpVersion
,
BgpConstants
.
BGP_VERSION
);
//
// ERROR: Unsupported Version Number
//
// Send NOTIFICATION and close the connection
int
errorCode
=
OpenMessageError
.
ERROR_CODE
;
int
errorSubcode
=
OpenMessageError
.
UNSUPPORTED_VERSION_NUMBER
;
ChannelBuffer
data
=
ChannelBuffers
.
buffer
(
2
);
data
.
writeShort
(
BgpConstants
.
BGP_VERSION
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
// Remote AS number
remoteAs
=
message
.
readUnsignedShort
();
//
// Verify that the AS number is same for all other BGP Sessions
// NOTE: This check applies only for our use-case where all BGP
// sessions are iBGP.
//
for
(
BgpSession
bgpSession
:
bgpSessionManager
.
getBgpSessions
())
{
if
(
remoteAs
!=
bgpSession
.
getRemoteAs
())
{
log
.
debug
(
"BGP RX OPEN Error from {}: Bad Peer AS {}. "
+
"Expected {}"
,
remoteAddress
,
remoteAs
,
bgpSession
.
getRemoteAs
());
//
// ERROR: Bad Peer AS
//
// Send NOTIFICATION and close the connection
int
errorCode
=
OpenMessageError
.
ERROR_CODE
;
int
errorSubcode
=
OpenMessageError
.
BAD_PEER_AS
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
}
// Remote Hold Time
remoteHoldtime
=
message
.
readUnsignedShort
();
if
((
remoteHoldtime
!=
0
)
&&
(
remoteHoldtime
<
BgpConstants
.
BGP_KEEPALIVE_MIN_HOLDTIME
))
{
log
.
debug
(
"BGP RX OPEN Error from {}: "
+
"Unacceptable Hold Time field {}. "
+
"Should be 0 or at least {}"
,
remoteAddress
,
remoteHoldtime
,
BgpConstants
.
BGP_KEEPALIVE_MIN_HOLDTIME
);
//
// ERROR: Unacceptable Hold Time
//
// Send NOTIFICATION and close the connection
int
errorCode
=
OpenMessageError
.
ERROR_CODE
;
int
errorSubcode
=
OpenMessageError
.
UNACCEPTABLE_HOLD_TIME
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
// Remote BGP Identifier
remoteBgpId
=
IpAddress
.
valueOf
((
int
)
message
.
readUnsignedInt
());
// Optional Parameters
int
optParamLen
=
message
.
readUnsignedByte
();
if
(
message
.
readableBytes
()
<
optParamLen
)
{
log
.
debug
(
"BGP RX OPEN Error from {}: "
+
"Invalid Optional Parameter Length field {}. "
+
"Remaining Optional Parameters {}"
,
remoteAddress
,
optParamLen
,
message
.
readableBytes
());
//
// ERROR: Invalid Optional Parameter Length field: Unspecific
//
// Send NOTIFICATION and close the connection
int
errorCode
=
OpenMessageError
.
ERROR_CODE
;
int
errorSubcode
=
Notifications
.
ERROR_SUBCODE_UNSPECIFIC
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
// TODO: Parse the optional parameters (if needed)
message
.
readBytes
(
optParamLen
);
// NOTE: data ignored
//
// Copy some of the remote peer's state/setup to the local setup:
// - BGP version
// - AS number (NOTE: the peer setup is always iBGP)
// - Holdtime
// Also, assign the local BGP ID based on the local setup
//
localBgpVersion
=
remoteBgpVersion
;
localAs
=
remoteAs
;
localHoldtime
=
remoteHoldtime
;
localBgpId
=
bgpSessionManager
.
getMyBgpId
();
// Set the Keepalive interval
if
(
localHoldtime
==
0
)
{
localKeepaliveInterval
=
0
;
}
else
{
localKeepaliveInterval
=
Math
.
max
(
localHoldtime
/
BgpConstants
.
BGP_KEEPALIVE_PER_HOLD_INTERVAL
,
BgpConstants
.
BGP_KEEPALIVE_MIN_INTERVAL
);
}
log
.
debug
(
"BGP RX OPEN message from {}: "
+
"BGPv{} AS {} BGP-ID {} Holdtime {}"
,
remoteAddress
,
remoteBgpVersion
,
remoteAs
,
remoteBgpId
,
remoteHoldtime
);
// Send my OPEN followed by KEEPALIVE
ChannelBuffer
txMessage
=
prepareBgpOpen
();
ctx
.
getChannel
().
write
(
txMessage
);
//
txMessage
=
prepareBgpKeepalive
();
ctx
.
getChannel
().
write
(
txMessage
);
// Start the KEEPALIVE timer
restartKeepaliveTimer
(
ctx
);
// Start the Session Timeout timer
restartSessionTimeoutTimer
(
ctx
);
}
/**
* Processes BGP UPDATE message.
*
* @param ctx the Channel Handler Context
* @param message the message to process
*/
void
processBgpUpdate
(
ChannelHandlerContext
ctx
,
ChannelBuffer
message
)
{
Collection
<
BgpRouteEntry
>
addedRoutes
=
null
;
Map
<
IpPrefix
,
BgpRouteEntry
>
deletedRoutes
=
new
HashMap
<>();
int
minLength
=
BgpConstants
.
BGP_UPDATE_MIN_LENGTH
-
BgpConstants
.
BGP_HEADER_LENGTH
;
if
(
message
.
readableBytes
()
<
minLength
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: "
+
"Message length {} too short. Must be at least {}"
,
remoteAddress
,
message
.
readableBytes
(),
minLength
);
//
// ERROR: Bad Message Length
//
// Send NOTIFICATION and close the connection
ChannelBuffer
txMessage
=
prepareBgpNotificationBadMessageLength
(
message
.
readableBytes
()
+
BgpConstants
.
BGP_HEADER_LENGTH
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
log
.
debug
(
"BGP RX UPDATE message from {}"
,
remoteAddress
);
//
// Parse the UPDATE message
//
//
// Parse the Withdrawn Routes
//
int
withdrawnRoutesLength
=
message
.
readUnsignedShort
();
if
(
withdrawnRoutesLength
>
message
.
readableBytes
())
{
// ERROR: Malformed Attribute List
actionsBgpUpdateMalformedAttributeList
(
ctx
);
return
;
}
Collection
<
IpPrefix
>
withdrawnPrefixes
=
null
;
try
{
withdrawnPrefixes
=
parsePackedPrefixes
(
withdrawnRoutesLength
,
message
);
}
catch
(
BgpParseException
e
)
{
// ERROR: Invalid Network Field
log
.
debug
(
"Exception parsing Withdrawn Prefixes from BGP peer {}: "
,
remoteBgpId
,
e
);
actionsBgpUpdateInvalidNetworkField
(
ctx
);
return
;
}
for
(
IpPrefix
prefix
:
withdrawnPrefixes
)
{
log
.
debug
(
"BGP RX UPDATE message WITHDRAWN from {}: {}"
,
remoteAddress
,
prefix
);
BgpRouteEntry
bgpRouteEntry
=
bgpRibIn
.
get
(
prefix
);
if
(
bgpRouteEntry
!=
null
)
{
deletedRoutes
.
put
(
prefix
,
bgpRouteEntry
);
}
}
//
// Parse the Path Attributes
//
try
{
addedRoutes
=
parsePathAttributes
(
ctx
,
message
);
}
catch
(
BgpParseException
e
)
{
log
.
debug
(
"Exception parsing Path Attributes from BGP peer {}: "
,
remoteBgpId
,
e
);
// NOTE: The session was already closed, so nothing else to do
return
;
}
// Ignore WITHDRAWN routes that are ADDED
for
(
BgpRouteEntry
bgpRouteEntry
:
addedRoutes
)
{
deletedRoutes
.
remove
(
bgpRouteEntry
.
prefix
());
}
// Update the BGP RIB-IN
for
(
BgpRouteEntry
bgpRouteEntry
:
deletedRoutes
.
values
())
{
bgpRibIn
.
remove
(
bgpRouteEntry
.
prefix
());
}
for
(
BgpRouteEntry
bgpRouteEntry
:
addedRoutes
)
{
bgpRibIn
.
put
(
bgpRouteEntry
.
prefix
(),
bgpRouteEntry
);
}
// Push the updates to the BGP Merged RIB
BgpSessionManager
.
BgpRouteSelector
bgpRouteSelector
=
bgpSessionManager
.
getBgpRouteSelector
();
bgpRouteSelector
.
routeUpdates
(
this
,
addedRoutes
,
deletedRoutes
.
values
());
// Start the Session Timeout timer
restartSessionTimeoutTimer
(
ctx
);
}
/**
* Parse BGP Path Attributes from the BGP UPDATE message.
*
* @param ctx the Channel Handler Context
* @param message the message to parse
* @return a collection of the result BGP Route Entries
* @throws BgpParseException
*/
private
Collection
<
BgpRouteEntry
>
parsePathAttributes
(
ChannelHandlerContext
ctx
,
ChannelBuffer
message
)
throws
BgpParseException
{
Map
<
IpPrefix
,
BgpRouteEntry
>
addedRoutes
=
new
HashMap
<>();
//
// Parsed values
//
Short
origin
=
-
1
;
// Mandatory
BgpRouteEntry
.
AsPath
asPath
=
null
;
// Mandatory
IpAddress
nextHop
=
null
;
// Mandatory
long
multiExitDisc
=
// Optional
BgpConstants
.
Update
.
MultiExitDisc
.
LOWEST_MULTI_EXIT_DISC
;
Long
localPref
=
null
;
// Mandatory
Long
aggregatorAsNumber
=
null
;
// Optional: unused
IpAddress
aggregatorIpAddress
=
null
;
// Optional: unused
//
// Get and verify the Path Attributes Length
//
int
pathAttributeLength
=
message
.
readUnsignedShort
();
if
(
pathAttributeLength
>
message
.
readableBytes
())
{
// ERROR: Malformed Attribute List
actionsBgpUpdateMalformedAttributeList
(
ctx
);
String
errorMsg
=
"Malformed Attribute List"
;
throw
new
BgpParseException
(
errorMsg
);
}
if
(
pathAttributeLength
==
0
)
{
return
addedRoutes
.
values
();
}
//
// Parse the Path Attributes
//
int
pathAttributeEnd
=
message
.
readerIndex
()
+
pathAttributeLength
;
while
(
message
.
readerIndex
()
<
pathAttributeEnd
)
{
int
attrFlags
=
message
.
readUnsignedByte
();
if
(
message
.
readerIndex
()
>=
pathAttributeEnd
)
{
// ERROR: Malformed Attribute List
actionsBgpUpdateMalformedAttributeList
(
ctx
);
String
errorMsg
=
"Malformed Attribute List"
;
throw
new
BgpParseException
(
errorMsg
);
}
int
attrTypeCode
=
message
.
readUnsignedByte
();
// The Attribute Flags
boolean
optionalBit
=
((
0x80
&
attrFlags
)
!=
0
);
boolean
transitiveBit
=
((
0x40
&
attrFlags
)
!=
0
);
boolean
partialBit
=
((
0x20
&
attrFlags
)
!=
0
);
boolean
extendedLengthBit
=
((
0x10
&
attrFlags
)
!=
0
);
// The Attribute Length
int
attrLen
=
0
;
int
attrLenOctets
=
1
;
if
(
extendedLengthBit
)
{
attrLenOctets
=
2
;
}
if
(
message
.
readerIndex
()
+
attrLenOctets
>
pathAttributeEnd
)
{
// ERROR: Malformed Attribute List
actionsBgpUpdateMalformedAttributeList
(
ctx
);
String
errorMsg
=
"Malformed Attribute List"
;
throw
new
BgpParseException
(
errorMsg
);
}
if
(
extendedLengthBit
)
{
attrLen
=
message
.
readUnsignedShort
();
}
else
{
attrLen
=
message
.
readUnsignedByte
();
}
if
(
message
.
readerIndex
()
+
attrLen
>
pathAttributeEnd
)
{
// ERROR: Malformed Attribute List
actionsBgpUpdateMalformedAttributeList
(
ctx
);
String
errorMsg
=
"Malformed Attribute List"
;
throw
new
BgpParseException
(
errorMsg
);
}
//
// Verify the Attribute Flags
//
verifyBgpUpdateAttributeFlags
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
//
// Extract the Attribute Value based on the Attribute Type Code
//
switch
(
attrTypeCode
)
{
case
BgpConstants
.
Update
.
Origin
.
TYPE
:
// Attribute Type Code ORIGIN
origin
=
parseAttributeTypeOrigin
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
break
;
case
BgpConstants
.
Update
.
AsPath
.
TYPE
:
// Attribute Type Code AS_PATH
asPath
=
parseAttributeTypeAsPath
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
break
;
case
BgpConstants
.
Update
.
NextHop
.
TYPE
:
// Attribute Type Code NEXT_HOP
nextHop
=
parseAttributeTypeNextHop
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
break
;
case
BgpConstants
.
Update
.
MultiExitDisc
.
TYPE
:
// Attribute Type Code MULTI_EXIT_DISC
multiExitDisc
=
parseAttributeTypeMultiExitDisc
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
break
;
case
BgpConstants
.
Update
.
LocalPref
.
TYPE
:
// Attribute Type Code LOCAL_PREF
localPref
=
parseAttributeTypeLocalPref
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
break
;
case
BgpConstants
.
Update
.
AtomicAggregate
.
TYPE
:
// Attribute Type Code ATOMIC_AGGREGATE
parseAttributeTypeAtomicAggregate
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
// Nothing to do: this attribute is primarily informational
break
;
case
BgpConstants
.
Update
.
Aggregator
.
TYPE
:
// Attribute Type Code AGGREGATOR
Pair
<
Long
,
IpAddress
>
aggregator
=
parseAttributeTypeAggregator
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
aggregatorAsNumber
=
aggregator
.
getLeft
();
aggregatorIpAddress
=
aggregator
.
getRight
();
break
;
default
:
// TODO: Parse any new Attribute Types if needed
if
(!
optionalBit
)
{
// ERROR: Unrecognized Well-known Attribute
actionsBgpUpdateUnrecognizedWellKnownAttribute
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Unrecognized Well-known Attribute: "
+
attrTypeCode
;
throw
new
BgpParseException
(
errorMsg
);
}
// Skip the data from the unrecognized attribute
log
.
debug
(
"BGP RX UPDATE message from {}: "
+
"Unrecognized Attribute Type {}"
,
remoteAddress
,
attrTypeCode
);
message
.
skipBytes
(
attrLen
);
break
;
}
}
//
// Verify the Well-known Attributes
//
verifyBgpUpdateWellKnownAttributes
(
ctx
,
origin
,
asPath
,
nextHop
,
localPref
);
//
// Parse the NLRI (Network Layer Reachability Information)
//
Collection
<
IpPrefix
>
addedPrefixes
=
null
;
int
nlriLength
=
message
.
readableBytes
();
try
{
addedPrefixes
=
parsePackedPrefixes
(
nlriLength
,
message
);
}
catch
(
BgpParseException
e
)
{
// ERROR: Invalid Network Field
log
.
debug
(
"Exception parsing NLRI from BGP peer {}: "
,
remoteBgpId
,
e
);
actionsBgpUpdateInvalidNetworkField
(
ctx
);
// Rethrow the exception
throw
e
;
}
// Generate the added routes
for
(
IpPrefix
prefix
:
addedPrefixes
)
{
BgpRouteEntry
bgpRouteEntry
=
new
BgpRouteEntry
(
this
,
prefix
,
nextHop
,
origin
.
byteValue
(),
asPath
,
localPref
);
bgpRouteEntry
.
setMultiExitDisc
(
multiExitDisc
);
if
(
bgpRouteEntry
.
hasAsPathLoop
(
localAs
))
{
log
.
debug
(
"BGP RX UPDATE message IGNORED from {}: {} "
+
"nextHop {}: contains AS Path loop"
,
remoteAddress
,
prefix
,
nextHop
);
continue
;
}
else
{
log
.
debug
(
"BGP RX UPDATE message ADDED from {}: {} nextHop {}"
,
remoteAddress
,
prefix
,
nextHop
);
}
addedRoutes
.
put
(
prefix
,
bgpRouteEntry
);
}
return
addedRoutes
.
values
();
}
/**
* Verifies BGP UPDATE Well-known Attributes.
*
* @param ctx the Channel Handler Context
* @param origin the ORIGIN well-known mandatory attribute
* @param asPath the AS_PATH well-known mandatory attribute
* @param nextHop the NEXT_HOP well-known mandatory attribute
* @param localPref the LOCAL_PREF required attribute
* @throws BgpParseException
*/
private
void
verifyBgpUpdateWellKnownAttributes
(
ChannelHandlerContext
ctx
,
Short
origin
,
BgpRouteEntry
.
AsPath
asPath
,
IpAddress
nextHop
,
Long
localPref
)
throws
BgpParseException
{
//
// Check for Missing Well-known Attributes
//
if
((
origin
==
null
)
||
(
origin
==
-
1
))
{
// Missing Attribute Type Code ORIGIN
int
type
=
BgpConstants
.
Update
.
Origin
.
TYPE
;
actionsBgpUpdateMissingWellKnownAttribute
(
ctx
,
type
);
String
errorMsg
=
"Missing Well-known Attribute: ORIGIN"
;
throw
new
BgpParseException
(
errorMsg
);
}
if
(
asPath
==
null
)
{
// Missing Attribute Type Code AS_PATH
int
type
=
BgpConstants
.
Update
.
AsPath
.
TYPE
;
actionsBgpUpdateMissingWellKnownAttribute
(
ctx
,
type
);
String
errorMsg
=
"Missing Well-known Attribute: AS_PATH"
;
throw
new
BgpParseException
(
errorMsg
);
}
if
(
nextHop
==
null
)
{
// Missing Attribute Type Code NEXT_HOP
int
type
=
BgpConstants
.
Update
.
NextHop
.
TYPE
;
actionsBgpUpdateMissingWellKnownAttribute
(
ctx
,
type
);
String
errorMsg
=
"Missing Well-known Attribute: NEXT_HOP"
;
throw
new
BgpParseException
(
errorMsg
);
}
if
(
localPref
==
null
)
{
// Missing Attribute Type Code LOCAL_PREF
// NOTE: Required for iBGP
int
type
=
BgpConstants
.
Update
.
LocalPref
.
TYPE
;
actionsBgpUpdateMissingWellKnownAttribute
(
ctx
,
type
);
String
errorMsg
=
"Missing Well-known Attribute: LOCAL_PREF"
;
throw
new
BgpParseException
(
errorMsg
);
}
}
/**
* Verifies the BGP UPDATE Attribute Flags.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @throws BgpParseException
*/
private
void
verifyBgpUpdateAttributeFlags
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
//
// Assign the Attribute Type Name and the Well-known flag
//
String
typeName
=
"UNKNOWN"
;
boolean
isWellKnown
=
false
;
switch
(
attrTypeCode
)
{
case
BgpConstants
.
Update
.
Origin
.
TYPE
:
isWellKnown
=
true
;
typeName
=
"ORIGIN"
;
break
;
case
BgpConstants
.
Update
.
AsPath
.
TYPE
:
isWellKnown
=
true
;
typeName
=
"AS_PATH"
;
break
;
case
BgpConstants
.
Update
.
NextHop
.
TYPE
:
isWellKnown
=
true
;
typeName
=
"NEXT_HOP"
;
break
;
case
BgpConstants
.
Update
.
MultiExitDisc
.
TYPE
:
isWellKnown
=
false
;
typeName
=
"MULTI_EXIT_DISC"
;
break
;
case
BgpConstants
.
Update
.
LocalPref
.
TYPE
:
isWellKnown
=
true
;
typeName
=
"LOCAL_PREF"
;
break
;
case
BgpConstants
.
Update
.
AtomicAggregate
.
TYPE
:
isWellKnown
=
true
;
typeName
=
"ATOMIC_AGGREGATE"
;
break
;
case
BgpConstants
.
Update
.
Aggregator
.
TYPE
:
isWellKnown
=
false
;
typeName
=
"AGGREGATOR"
;
break
;
default
:
isWellKnown
=
false
;
typeName
=
"UNKNOWN("
+
attrTypeCode
+
")"
;
break
;
}
//
// Verify the Attribute Flags
//
boolean
optionalBit
=
((
0x80
&
attrFlags
)
!=
0
);
boolean
transitiveBit
=
((
0x40
&
attrFlags
)
!=
0
);
boolean
partialBit
=
((
0x20
&
attrFlags
)
!=
0
);
if
((
isWellKnown
&&
optionalBit
)
||
(
isWellKnown
&&
(!
transitiveBit
))
||
(
isWellKnown
&&
partialBit
)
||
(
optionalBit
&&
(!
transitiveBit
)
&&
partialBit
))
{
//
// ERROR: The Optional bit cannot be set for Well-known attributes
// ERROR: The Transtive bit MUST be 1 for well-known attributes
// ERROR: The Partial bit MUST be 0 for well-known attributes
// ERROR: The Partial bit MUST be 0 for optional non-transitive
// attributes
//
actionsBgpUpdateAttributeFlagsError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Flags Error for "
+
typeName
+
": "
+
attrFlags
;
throw
new
BgpParseException
(
errorMsg
);
}
}
/**
* Parses BGP UPDATE Attribute Type ORIGIN.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed ORIGIN value
* @throws BgpParseException
*/
private
short
parseAttributeTypeOrigin
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
Origin
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
message
.
markReaderIndex
();
short
origin
=
message
.
readUnsignedByte
();
switch
(
origin
)
{
case
BgpConstants
.
Update
.
Origin
.
IGP
:
// FALLTHROUGH
case
BgpConstants
.
Update
.
Origin
.
EGP
:
// FALLTHROUGH
case
BgpConstants
.
Update
.
Origin
.
INCOMPLETE
:
break
;
default
:
// ERROR: Invalid ORIGIN Attribute
message
.
resetReaderIndex
();
actionsBgpUpdateInvalidOriginAttribute
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
,
origin
);
String
errorMsg
=
"Invalid ORIGIN Attribute: "
+
origin
;
throw
new
BgpParseException
(
errorMsg
);
}
return
origin
;
}
/**
* Parses BGP UPDATE Attribute AS Path.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed AS Path
* @throws BgpParseException
*/
private
BgpRouteEntry
.
AsPath
parseAttributeTypeAsPath
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
ArrayList
<
BgpRouteEntry
.
PathSegment
>
pathSegments
=
new
ArrayList
<>();
//
// Parse the message
//
while
(
attrLen
>
0
)
{
if
(
attrLen
<
2
)
{
// ERROR: Malformed AS_PATH
actionsBgpUpdateMalformedAsPath
(
ctx
);
String
errorMsg
=
"Malformed AS Path"
;
throw
new
BgpParseException
(
errorMsg
);
}
// Get the Path Segment Type and Length (in number of ASes)
short
pathSegmentType
=
message
.
readUnsignedByte
();
short
pathSegmentLength
=
message
.
readUnsignedByte
();
attrLen
-=
2
;
// Verify the Path Segment Type
switch
(
pathSegmentType
)
{
case
BgpConstants
.
Update
.
AsPath
.
AS_SET
:
// FALLTHROUGH
case
BgpConstants
.
Update
.
AsPath
.
AS_SEQUENCE
:
break
;
default
:
// ERROR: Invalid Path Segment Type
//
// NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
// for "Invalid Path Segment Type", hence we return
// the error as "Malformed AS_PATH".
//
actionsBgpUpdateMalformedAsPath
(
ctx
);
String
errorMsg
=
"Invalid AS Path Segment Type: "
+
pathSegmentType
;
throw
new
BgpParseException
(
errorMsg
);
}
// Parse the AS numbers
if
(
2
*
pathSegmentLength
>
attrLen
)
{
// ERROR: Malformed AS_PATH
actionsBgpUpdateMalformedAsPath
(
ctx
);
String
errorMsg
=
"Malformed AS Path"
;
throw
new
BgpParseException
(
errorMsg
);
}
attrLen
-=
(
2
*
pathSegmentLength
);
ArrayList
<
Long
>
segmentAsNumbers
=
new
ArrayList
<>();
while
(
pathSegmentLength
--
>
0
)
{
long
asNumber
=
message
.
readUnsignedShort
();
segmentAsNumbers
.
add
(
asNumber
);
}
BgpRouteEntry
.
PathSegment
pathSegment
=
new
BgpRouteEntry
.
PathSegment
((
byte
)
pathSegmentType
,
segmentAsNumbers
);
pathSegments
.
add
(
pathSegment
);
}
return
new
BgpRouteEntry
.
AsPath
(
pathSegments
);
}
/**
* Parses BGP UPDATE Attribute Type NEXT_HOP.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed NEXT_HOP value
* @throws BgpParseException
*/
private
IpAddress
parseAttributeTypeNextHop
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
NextHop
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
message
.
markReaderIndex
();
long
address
=
message
.
readUnsignedInt
();
IpAddress
nextHopAddress
=
IpAddress
.
valueOf
((
int
)
address
);
//
// Check whether the NEXT_HOP IP address is semantically correct.
// As per RFC 4271, Section 6.3:
//
// a) It MUST NOT be the IP address of the receiving speaker
// b) In the case of an EBGP ....
//
// Here we check only (a), because (b) doesn't apply for us: all our
// peers are iBGP.
//
if
(
nextHopAddress
.
equals
(
localIp4Address
))
{
// ERROR: Invalid NEXT_HOP Attribute
message
.
resetReaderIndex
();
actionsBgpUpdateInvalidNextHopAttribute
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
,
nextHopAddress
);
String
errorMsg
=
"Invalid NEXT_HOP Attribute: "
+
nextHopAddress
;
throw
new
BgpParseException
(
errorMsg
);
}
return
nextHopAddress
;
}
/**
* Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed MULTI_EXIT_DISC value
* @throws BgpParseException
*/
private
long
parseAttributeTypeMultiExitDisc
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
MultiExitDisc
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
long
multiExitDisc
=
message
.
readUnsignedInt
();
return
multiExitDisc
;
}
/**
* Parses BGP UPDATE Attribute Type LOCAL_PREF.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed LOCAL_PREF value
* @throws BgpParseException
*/
private
long
parseAttributeTypeLocalPref
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
LocalPref
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
long
localPref
=
message
.
readUnsignedInt
();
return
localPref
;
}
/**
* Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @throws BgpParseException
*/
private
void
parseAttributeTypeAtomicAggregate
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
AtomicAggregate
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
// Nothing to do: this attribute is primarily informational
}
/**
* Parses BGP UPDATE Attribute Type AGGREGATOR.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message to parse
* @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
* @throws BgpParseException
*/
private
Pair
<
Long
,
IpAddress
>
parseAttributeTypeAggregator
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
throws
BgpParseException
{
// Check the Attribute Length
if
(
attrLen
!=
BgpConstants
.
Update
.
Aggregator
.
LENGTH
)
{
// ERROR: Attribute Length Error
actionsBgpUpdateAttributeLengthError
(
ctx
,
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
String
errorMsg
=
"Attribute Length Error"
;
throw
new
BgpParseException
(
errorMsg
);
}
// The AGGREGATOR AS number
long
aggregatorAsNumber
=
message
.
readUnsignedShort
();
// The AGGREGATOR IP address
long
aggregatorAddress
=
message
.
readUnsignedInt
();
IpAddress
aggregatorIpAddress
=
IpAddress
.
valueOf
((
int
)
aggregatorAddress
);
Pair
<
Long
,
IpAddress
>
aggregator
=
Pair
.
of
(
aggregatorAsNumber
,
aggregatorIpAddress
);
return
aggregator
;
}
/**
* Parses a message that contains encoded IPv4 network prefixes.
* <p>
* The IPv4 prefixes are encoded in the form:
* <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
* and Prefix is the IPv4 prefix (padded with trailing bits to the end
* of an octet).
*
* @param totalLength the total length of the data to parse
* @param message the message with data to parse
* @return a collection of parsed IPv4 network prefixes
* @throws BgpParseException
*/
private
Collection
<
IpPrefix
>
parsePackedPrefixes
(
int
totalLength
,
ChannelBuffer
message
)
throws
BgpParseException
{
Collection
<
IpPrefix
>
result
=
new
ArrayList
<>();
if
(
totalLength
==
0
)
{
return
result
;
}
// Parse the data
int
dataEnd
=
message
.
readerIndex
()
+
totalLength
;
while
(
message
.
readerIndex
()
<
dataEnd
)
{
int
prefixBitlen
=
message
.
readUnsignedByte
();
int
prefixBytelen
=
(
prefixBitlen
+
7
)
/
8
;
// Round-up
if
(
message
.
readerIndex
()
+
prefixBytelen
>
dataEnd
)
{
String
errorMsg
=
"Malformed Network Prefixes"
;
throw
new
BgpParseException
(
errorMsg
);
}
long
address
=
0
;
long
extraShift
=
(
4
-
prefixBytelen
)
*
8
;
while
(
prefixBytelen
>
0
)
{
address
<<=
8
;
address
|=
message
.
readUnsignedByte
();
prefixBytelen
--;
}
address
<<=
extraShift
;
IpPrefix
prefix
=
IpPrefix
.
valueOf
(
IpAddress
.
valueOf
((
int
)
address
).
toRealInt
(),
(
short
)
prefixBitlen
);
result
.
add
(
prefix
);
}
return
result
;
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Invalid Network Field Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
*/
private
void
actionsBgpUpdateInvalidNetworkField
(
ChannelHandlerContext
ctx
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Invalid Network Field"
,
remoteAddress
);
//
// ERROR: Invalid Network Field
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
INVALID_NETWORK_FIELD
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Malformed Attribute List Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
*/
private
void
actionsBgpUpdateMalformedAttributeList
(
ChannelHandlerContext
ctx
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Malformed Attribute List"
,
remoteAddress
);
//
// ERROR: Malformed Attribute List
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
MALFORMED_ATTRIBUTE_LIST
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Missing Well-known Attribute Error: send NOTIFICATION and close the
* channel.
*
* @param ctx the Channel Handler Context
* @param missingAttrTypeCode the missing attribute type code
*/
private
void
actionsBgpUpdateMissingWellKnownAttribute
(
ChannelHandlerContext
ctx
,
int
missingAttrTypeCode
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}"
,
remoteAddress
,
missingAttrTypeCode
);
//
// ERROR: Missing Well-known Attribute
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
MISSING_WELL_KNOWN_ATTRIBUTE
;
ChannelBuffer
data
=
ChannelBuffers
.
buffer
(
1
);
data
.
writeByte
(
missingAttrTypeCode
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
* @param origin the ORIGIN attribute value
*/
private
void
actionsBgpUpdateInvalidOriginAttribute
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
,
short
origin
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute"
,
remoteAddress
);
//
// ERROR: Invalid ORIGIN Attribute
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
INVALID_ORIGIN_ATTRIBUTE
;
ChannelBuffer
data
=
prepareBgpUpdateNotificationDataPayload
(
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Attribute Flags Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
*/
private
void
actionsBgpUpdateAttributeFlagsError
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Attribute Flags Error"
,
remoteAddress
);
//
// ERROR: Attribute Flags Error
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
ATTRIBUTE_FLAGS_ERROR
;
ChannelBuffer
data
=
prepareBgpUpdateNotificationDataPayload
(
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
* channel.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
* @param nextHop the NEXT_HOP attribute value
*/
private
void
actionsBgpUpdateInvalidNextHopAttribute
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
,
IpAddress
nextHop
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}"
,
remoteAddress
,
nextHop
);
//
// ERROR: Invalid ORIGIN Attribute
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
INVALID_NEXT_HOP_ATTRIBUTE
;
ChannelBuffer
data
=
prepareBgpUpdateNotificationDataPayload
(
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Unrecognized Well-known Attribute Error: send NOTIFICATION and close
* the channel.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
*/
private
void
actionsBgpUpdateUnrecognizedWellKnownAttribute
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: "
+
"Unrecognized Well-known Attribute Error: {}"
,
remoteAddress
,
attrTypeCode
);
//
// ERROR: Unrecognized Well-known Attribute
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE
;
ChannelBuffer
data
=
prepareBgpUpdateNotificationDataPayload
(
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Attribute Length Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
*/
private
void
actionsBgpUpdateAttributeLengthError
(
ChannelHandlerContext
ctx
,
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Attribute Length Error"
,
remoteAddress
);
//
// ERROR: Attribute Length Error
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
ATTRIBUTE_LENGTH_ERROR
;
ChannelBuffer
data
=
prepareBgpUpdateNotificationDataPayload
(
attrTypeCode
,
attrLen
,
attrFlags
,
message
);
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Applies the appropriate actions after detecting BGP UPDATE
* Malformed AS_PATH Error: send NOTIFICATION and close the channel.
*
* @param ctx the Channel Handler Context
*/
private
void
actionsBgpUpdateMalformedAsPath
(
ChannelHandlerContext
ctx
)
{
log
.
debug
(
"BGP RX UPDATE Error from {}: Malformed AS Path"
,
remoteAddress
);
//
// ERROR: Malformed AS_PATH
//
// Send NOTIFICATION and close the connection
int
errorCode
=
UpdateMessageError
.
ERROR_CODE
;
int
errorSubcode
=
UpdateMessageError
.
MALFORMED_AS_PATH
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
/**
* Processes BGP NOTIFICATION message.
*
* @param ctx the Channel Handler Context
* @param message the message to process
*/
void
processBgpNotification
(
ChannelHandlerContext
ctx
,
ChannelBuffer
message
)
{
int
minLength
=
BgpConstants
.
BGP_NOTIFICATION_MIN_LENGTH
-
BgpConstants
.
BGP_HEADER_LENGTH
;
if
(
message
.
readableBytes
()
<
minLength
)
{
log
.
debug
(
"BGP RX NOTIFICATION Error from {}: "
+
"Message length {} too short. Must be at least {}"
,
remoteAddress
,
message
.
readableBytes
(),
minLength
);
//
// ERROR: Bad Message Length
//
// NOTE: We do NOT send NOTIFICATION in response to a notification
return
;
}
//
// Parse the NOTIFICATION message
//
int
errorCode
=
message
.
readUnsignedByte
();
int
errorSubcode
=
message
.
readUnsignedByte
();
int
dataLength
=
message
.
readableBytes
();
log
.
debug
(
"BGP RX NOTIFICATION message from {}: Error Code {} "
+
"Error Subcode {} Data Length {}"
,
remoteAddress
,
errorCode
,
errorSubcode
,
dataLength
);
//
// NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to
// close the connection.
//
// Start the Session Timeout timer
restartSessionTimeoutTimer
(
ctx
);
}
/**
* Processes BGP KEEPALIVE message.
*
* @param ctx the Channel Handler Context
* @param message the message to process
*/
void
processBgpKeepalive
(
ChannelHandlerContext
ctx
,
ChannelBuffer
message
)
{
if
(
message
.
readableBytes
()
+
BgpConstants
.
BGP_HEADER_LENGTH
!=
BgpConstants
.
BGP_KEEPALIVE_EXPECTED_LENGTH
)
{
log
.
debug
(
"BGP RX KEEPALIVE Error from {}: "
+
"Invalid total message length {}. Expected {}"
,
remoteAddress
,
message
.
readableBytes
()
+
BgpConstants
.
BGP_HEADER_LENGTH
,
BgpConstants
.
BGP_KEEPALIVE_EXPECTED_LENGTH
);
//
// ERROR: Bad Message Length
//
// Send NOTIFICATION and close the connection
ChannelBuffer
txMessage
=
prepareBgpNotificationBadMessageLength
(
message
.
readableBytes
()
+
BgpConstants
.
BGP_HEADER_LENGTH
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
return
;
}
//
// Parse the KEEPALIVE message: nothing to do
//
log
.
debug
(
"BGP RX KEEPALIVE message from {}"
,
remoteAddress
);
// Start the Session Timeout timer
restartSessionTimeoutTimer
(
ctx
);
}
/**
* Prepares BGP OPEN message.
*
* @return the message to transmit (BGP header included)
*/
private
ChannelBuffer
prepareBgpOpen
()
{
ChannelBuffer
message
=
ChannelBuffers
.
buffer
(
BgpConstants
.
BGP_MESSAGE_MAX_LENGTH
);
//
// Prepare the OPEN message payload
//
message
.
writeByte
(
localBgpVersion
);
message
.
writeShort
((
int
)
localAs
);
message
.
writeShort
((
int
)
localHoldtime
);
message
.
writeInt
(
bgpSessionManager
.
getMyBgpId
().
toRealInt
());
message
.
writeByte
(
0
);
// No Optional Parameters
return
prepareBgpMessage
(
BgpConstants
.
BGP_TYPE_OPEN
,
message
);
}
/**
* Prepares BGP KEEPALIVE message.
*
* @return the message to transmit (BGP header included)
*/
private
ChannelBuffer
prepareBgpKeepalive
()
{
ChannelBuffer
message
=
ChannelBuffers
.
buffer
(
BgpConstants
.
BGP_MESSAGE_MAX_LENGTH
);
//
// Prepare the KEEPALIVE message payload: nothing to do
//
return
prepareBgpMessage
(
BgpConstants
.
BGP_TYPE_KEEPALIVE
,
message
);
}
/**
* Prepares BGP NOTIFICATION message.
*
* @param errorCode the BGP NOTIFICATION Error Code
* @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
* otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
* @param payload the BGP NOTIFICATION Data if applicable, otherwise null
* @return the message to transmit (BGP header included)
*/
ChannelBuffer
prepareBgpNotification
(
int
errorCode
,
int
errorSubcode
,
ChannelBuffer
data
)
{
ChannelBuffer
message
=
ChannelBuffers
.
buffer
(
BgpConstants
.
BGP_MESSAGE_MAX_LENGTH
);
//
// Prepare the NOTIFICATION message payload
//
message
.
writeByte
(
errorCode
);
message
.
writeByte
(
errorSubcode
);
if
(
data
!=
null
)
{
message
.
writeBytes
(
data
);
}
return
prepareBgpMessage
(
BgpConstants
.
BGP_TYPE_NOTIFICATION
,
message
);
}
/**
* Prepares BGP NOTIFICATION message: Bad Message Length.
*
* @param length the erroneous Length field
* @return the message to transmit (BGP header included)
*/
ChannelBuffer
prepareBgpNotificationBadMessageLength
(
int
length
)
{
int
errorCode
=
MessageHeaderError
.
ERROR_CODE
;
int
errorSubcode
=
MessageHeaderError
.
BAD_MESSAGE_LENGTH
;
ChannelBuffer
data
=
ChannelBuffers
.
buffer
(
2
);
data
.
writeShort
(
length
);
return
prepareBgpNotification
(
errorCode
,
errorSubcode
,
data
);
}
/**
* Prepares BGP UPDATE Notification data payload.
*
* @param attrTypeCode the attribute type code
* @param attrLen the attribute length (in octets)
* @param attrFlags the attribute flags
* @param message the message with the data
* @return the buffer with the data payload for the BGP UPDATE Notification
*/
private
ChannelBuffer
prepareBgpUpdateNotificationDataPayload
(
int
attrTypeCode
,
int
attrLen
,
int
attrFlags
,
ChannelBuffer
message
)
{
// Compute the attribute length field octets
boolean
extendedLengthBit
=
((
0x10
&
attrFlags
)
!=
0
);
int
attrLenOctets
=
1
;
if
(
extendedLengthBit
)
{
attrLenOctets
=
2
;
}
ChannelBuffer
data
=
ChannelBuffers
.
buffer
(
attrLen
+
attrLenOctets
+
1
);
data
.
writeByte
(
attrTypeCode
);
if
(
extendedLengthBit
)
{
data
.
writeShort
(
attrLen
);
}
else
{
data
.
writeByte
(
attrLen
);
}
data
.
writeBytes
(
message
,
attrLen
);
return
data
;
}
/**
* Prepares BGP message.
*
* @param type the BGP message type
* @param payload the message payload to transmit (BGP header excluded)
* @return the message to transmit (BGP header included)
*/
private
ChannelBuffer
prepareBgpMessage
(
int
type
,
ChannelBuffer
payload
)
{
ChannelBuffer
message
=
ChannelBuffers
.
buffer
(
BgpConstants
.
BGP_HEADER_LENGTH
+
payload
.
readableBytes
());
// Write the marker
for
(
int
i
=
0
;
i
<
BgpConstants
.
BGP_HEADER_MARKER_LENGTH
;
i
++)
{
message
.
writeByte
(
0xff
);
}
// Write the rest of the BGP header
message
.
writeShort
(
BgpConstants
.
BGP_HEADER_LENGTH
+
payload
.
readableBytes
());
message
.
writeByte
(
type
);
// Write the payload
message
.
writeBytes
(
payload
);
return
message
;
}
/**
* Restarts the BGP KeepaliveTimer.
*/
private
void
restartKeepaliveTimer
(
ChannelHandlerContext
ctx
)
{
if
(
localKeepaliveInterval
==
0
)
{
return
;
// Nothing to do
}
keepaliveTimeout
=
timer
.
newTimeout
(
new
TransmitKeepaliveTask
(
ctx
),
localKeepaliveInterval
,
TimeUnit
.
SECONDS
);
}
/**
* Task class for transmitting KEEPALIVE messages.
*/
private
final
class
TransmitKeepaliveTask
implements
TimerTask
{
private
final
ChannelHandlerContext
ctx
;
/**
* Constructor for given Channel Handler Context.
*
* @param ctx the Channel Handler Context to use
*/
TransmitKeepaliveTask
(
ChannelHandlerContext
ctx
)
{
this
.
ctx
=
ctx
;
}
@Override
public
void
run
(
Timeout
timeout
)
throws
Exception
{
if
(
timeout
.
isCancelled
())
{
return
;
}
if
(!
ctx
.
getChannel
().
isOpen
())
{
return
;
}
// Transmit the KEEPALIVE
ChannelBuffer
txMessage
=
prepareBgpKeepalive
();
ctx
.
getChannel
().
write
(
txMessage
);
// Restart the KEEPALIVE timer
restartKeepaliveTimer
(
ctx
);
}
}
/**
* Restarts the BGP Session Timeout Timer.
*/
private
void
restartSessionTimeoutTimer
(
ChannelHandlerContext
ctx
)
{
if
(
remoteHoldtime
==
0
)
{
return
;
// Nothing to do
}
if
(
sessionTimeout
!=
null
)
{
sessionTimeout
.
cancel
();
}
sessionTimeout
=
timer
.
newTimeout
(
new
SessionTimeoutTask
(
ctx
),
remoteHoldtime
,
TimeUnit
.
SECONDS
);
}
/**
* Task class for BGP Session timeout.
*/
private
final
class
SessionTimeoutTask
implements
TimerTask
{
private
final
ChannelHandlerContext
ctx
;
/**
* Constructor for given Channel Handler Context.
*
* @param ctx the Channel Handler Context to use
*/
SessionTimeoutTask
(
ChannelHandlerContext
ctx
)
{
this
.
ctx
=
ctx
;
}
@Override
public
void
run
(
Timeout
timeout
)
throws
Exception
{
if
(
timeout
.
isCancelled
())
{
return
;
}
if
(!
ctx
.
getChannel
().
isOpen
())
{
return
;
}
log
.
debug
(
"BGP Session Timeout: peer {}"
,
remoteAddress
);
//
// ERROR: Invalid Optional Parameter Length field: Unspecific
//
// Send NOTIFICATION and close the connection
int
errorCode
=
HoldTimerExpired
.
ERROR_CODE
;
int
errorSubcode
=
Notifications
.
ERROR_SUBCODE_UNSPECIFIC
;
ChannelBuffer
txMessage
=
prepareBgpNotification
(
errorCode
,
errorSubcode
,
null
);
ctx
.
getChannel
().
write
(
txMessage
);
closeChannel
(
ctx
);
}
}
/**
* An exception indicating a parsing error of the BGP message.
*/
private
static
class
BgpParseException
extends
Exception
{
/**
* Default constructor.
*/
public
BgpParseException
()
{
super
();
}
/**
* Constructor for a specific exception details message.
*
* @param message the message with the exception details
*/
public
BgpParseException
(
String
message
)
{
super
(
message
);
}
}
}
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/BgpSessionManager.java
0 → 100644
View file @
ab63aac
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
import
static
com
.
google
.
common
.
base
.
Preconditions
.
checkNotNull
;
import
java.net.InetAddress
;
import
java.net.InetSocketAddress
;
import
java.net.SocketAddress
;
import
java.util.Collection
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentMap
;
import
java.util.concurrent.Executors
;
import
org.jboss.netty.bootstrap.ServerBootstrap
;
import
org.jboss.netty.channel.Channel
;
import
org.jboss.netty.channel.ChannelException
;
import
org.jboss.netty.channel.ChannelFactory
;
import
org.jboss.netty.channel.ChannelPipeline
;
import
org.jboss.netty.channel.ChannelPipelineFactory
;
import
org.jboss.netty.channel.Channels
;
import
org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
;
import
org.onlab.onos.sdnip.RouteListener
;
import
org.onlab.onos.sdnip.RouteUpdate
;
import
org.onlab.packet.IpAddress
;
import
org.onlab.packet.IpPrefix
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* BGP Session Manager class.
*/
public
class
BgpSessionManager
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
BgpSessionManager
.
class
);
private
Channel
serverChannel
;
// Listener for incoming BGP connections
private
ConcurrentMap
<
SocketAddress
,
BgpSession
>
bgpSessions
=
new
ConcurrentHashMap
<>();
private
IpAddress
myBgpId
;
// Same BGP ID for all peers
private
BgpRouteSelector
bgpRouteSelector
=
new
BgpRouteSelector
();
private
ConcurrentMap
<
IpPrefix
,
BgpRouteEntry
>
bgpRoutes
=
new
ConcurrentHashMap
<>();
private
final
RouteListener
routeListener
;
/**
* Constructor for given route listener.
*
* @param routeListener the route listener to use
*/
public
BgpSessionManager
(
RouteListener
routeListener
)
{
this
.
routeListener
=
checkNotNull
(
routeListener
);
}
/**
* Gets the BGP sessions.
*
* @return the BGP sessions
*/
public
Collection
<
BgpSession
>
getBgpSessions
()
{
return
bgpSessions
.
values
();
}
/**
* Gets the BGP routes.
*
* @return the BGP routes
*/
public
Collection
<
BgpRouteEntry
>
getBgpRoutes
()
{
return
bgpRoutes
.
values
();
}
/**
* Processes the connection from a BGP peer.
*
* @param bgpSession the BGP session for the peer
* @return true if the connection can be established, otherwise false
*/
boolean
peerConnected
(
BgpSession
bgpSession
)
{
// Test whether there is already a session from the same remote
if
(
bgpSessions
.
get
(
bgpSession
.
getRemoteAddress
())
!=
null
)
{
return
false
;
// Duplicate BGP session
}
bgpSessions
.
put
(
bgpSession
.
getRemoteAddress
(),
bgpSession
);
//
// If the first connection, set my BGP ID to the local address
// of the socket.
//
if
(
bgpSession
.
getLocalAddress
()
instanceof
InetSocketAddress
)
{
InetAddress
inetAddr
=
((
InetSocketAddress
)
bgpSession
.
getLocalAddress
()).
getAddress
();
IpAddress
ip4Address
=
IpAddress
.
valueOf
(
inetAddr
.
getAddress
());
updateMyBgpId
(
ip4Address
);
}
return
true
;
}
/**
* Processes the disconnection from a BGP peer.
*
* @param bgpSession the BGP session for the peer
*/
void
peerDisconnected
(
BgpSession
bgpSession
)
{
bgpSessions
.
remove
(
bgpSession
.
getRemoteAddress
());
}
/**
* Conditionally updates the local BGP ID if it wasn't set already.
* <p/>
* NOTE: A BGP instance should use same BGP ID across all BGP sessions.
*
* @param ip4Address the IPv4 address to use as BGP ID
*/
private
synchronized
void
updateMyBgpId
(
IpAddress
ip4Address
)
{
if
(
myBgpId
==
null
)
{
myBgpId
=
ip4Address
;
log
.
debug
(
"BGP: My BGP ID is {}"
,
myBgpId
);
}
}
/**
* Gets the local BGP Identifier as an IPv4 address.
*
* @return the local BGP Identifier as an IPv4 address
*/
IpAddress
getMyBgpId
()
{
return
myBgpId
;
}
/**
* Gets the BGP Route Selector.
*
* @return the BGP Route Selector
*/
BgpRouteSelector
getBgpRouteSelector
()
{
return
bgpRouteSelector
;
}
/**
* Starts up BGP Session Manager operation.
*
* @param listenPortNumber the port number to listen on. By default
* it should be BgpConstants.BGP_PORT (179)
*/
public
void
startUp
(
int
listenPortNumber
)
{
log
.
debug
(
"BGP Session Manager startUp()"
);
ChannelFactory
channelFactory
=
new
NioServerSocketChannelFactory
(
Executors
.
newCachedThreadPool
(),
Executors
.
newCachedThreadPool
());
ChannelPipelineFactory
pipelineFactory
=
new
ChannelPipelineFactory
()
{
@Override
public
ChannelPipeline
getPipeline
()
throws
Exception
{
// Allocate a new session per connection
BgpSession
bgpSessionHandler
=
new
BgpSession
(
BgpSessionManager
.
this
);
BgpFrameDecoder
bgpFrameDecoder
=
new
BgpFrameDecoder
(
bgpSessionHandler
);
// Setup the processing pipeline
ChannelPipeline
pipeline
=
Channels
.
pipeline
();
pipeline
.
addLast
(
"BgpFrameDecoder"
,
bgpFrameDecoder
);
pipeline
.
addLast
(
"BgpSession"
,
bgpSessionHandler
);
return
pipeline
;
}
};
InetSocketAddress
listenAddress
=
new
InetSocketAddress
(
listenPortNumber
);
ServerBootstrap
serverBootstrap
=
new
ServerBootstrap
(
channelFactory
);
// serverBootstrap.setOptions("reuseAddr", true);
serverBootstrap
.
setOption
(
"child.keepAlive"
,
true
);
serverBootstrap
.
setOption
(
"child.tcpNoDelay"
,
true
);
serverBootstrap
.
setPipelineFactory
(
pipelineFactory
);
try
{
serverChannel
=
serverBootstrap
.
bind
(
listenAddress
);
}
catch
(
ChannelException
e
)
{
log
.
debug
(
"Exception binding to BGP port {}: "
,
listenAddress
.
getPort
(),
e
);
}
}
/**
* Shuts down the BGP Session Manager operation.
*/
public
void
shutDown
()
{
// TODO: Complete the implementation: remove routes, etc.
if
(
serverChannel
!=
null
)
{
serverChannel
.
close
();
}
}
/**
* Class to receive and process the BGP routes from each BGP Session/Peer.
*/
class
BgpRouteSelector
{
/**
* Processes route entry updates: added/updated and deleted route
* entries.
*
* @param bgpSession the BGP session the route entry updates were
* received on
* @param addedBgpRouteEntries the added/updated route entries to
* process
* @param deletedBgpRouteEntries the deleted route entries to process
*/
synchronized
void
routeUpdates
(
BgpSession
bgpSession
,
Collection
<
BgpRouteEntry
>
addedBgpRouteEntries
,
Collection
<
BgpRouteEntry
>
deletedBgpRouteEntries
)
{
//
// TODO: Merge the updates from different BGP Peers,
// by choosing the best route.
//
// Process the deleted route entries
for
(
BgpRouteEntry
bgpRouteEntry
:
deletedBgpRouteEntries
)
{
processDeletedRoute
(
bgpSession
,
bgpRouteEntry
);
}
// Process the added/updated route entries
for
(
BgpRouteEntry
bgpRouteEntry
:
addedBgpRouteEntries
)
{
processAddedRoute
(
bgpSession
,
bgpRouteEntry
);
}
}
/**
* Processes an added/updated route entry.
*
* @param bgpSession the BGP session the route entry update was
* received on
* @param bgpRouteEntry the added/updated route entry
*/
private
void
processAddedRoute
(
BgpSession
bgpSession
,
BgpRouteEntry
bgpRouteEntry
)
{
RouteUpdate
routeUpdate
;
BgpRouteEntry
bestBgpRouteEntry
=
bgpRoutes
.
get
(
bgpRouteEntry
.
prefix
());
//
// Install the new route entry if it is better than the
// current best route.
//
if
((
bestBgpRouteEntry
==
null
)
||
bgpRouteEntry
.
isBetterThan
(
bestBgpRouteEntry
))
{
bgpRoutes
.
put
(
bgpRouteEntry
.
prefix
(),
bgpRouteEntry
);
routeUpdate
=
new
RouteUpdate
(
RouteUpdate
.
Type
.
UPDATE
,
bgpRouteEntry
);
// Forward the result route updates to the Route Listener
routeListener
.
update
(
routeUpdate
);
return
;
}
//
// If the route entry arrived on the same BGP Session as
// the current best route, then elect the next best route
// and install it.
//
if
(
bestBgpRouteEntry
.
getBgpSession
()
!=
bgpRouteEntry
.
getBgpSession
())
{
return
;
}
// Find the next best route
bestBgpRouteEntry
=
findBestBgpRoute
(
bgpRouteEntry
.
prefix
());
if
(
bestBgpRouteEntry
==
null
)
{
//
// TODO: Shouldn't happen. Install the new route as a
// pre-caution.
//
log
.
debug
(
"BGP next best route for prefix {} is missing. "
+
"Adding the route that is currently processed."
,
bgpRouteEntry
.
prefix
());
bestBgpRouteEntry
=
bgpRouteEntry
;
}
// Install the next best route
bgpRoutes
.
put
(
bestBgpRouteEntry
.
prefix
(),
bestBgpRouteEntry
);
routeUpdate
=
new
RouteUpdate
(
RouteUpdate
.
Type
.
UPDATE
,
bestBgpRouteEntry
);
// Forward the result route updates to the Route Listener
routeListener
.
update
(
routeUpdate
);
}
/**
* Processes a deleted route entry.
*
* @param bgpSession the BGP session the route entry update was
* received on
* @param bgpRouteEntry the deleted route entry
*/
private
void
processDeletedRoute
(
BgpSession
bgpSession
,
BgpRouteEntry
bgpRouteEntry
)
{
RouteUpdate
routeUpdate
;
BgpRouteEntry
bestBgpRouteEntry
=
bgpRoutes
.
get
(
bgpRouteEntry
.
prefix
());
//
// Remove the route entry only if it was the best one.
// Install the the next best route if it exists.
//
// NOTE: We intentionally use "==" instead of method equals(),
// because we need to check whether this is same object.
//
if
(
bgpRouteEntry
!=
bestBgpRouteEntry
)
{
return
;
// Nothing to do
}
//
// Find the next best route
//
bestBgpRouteEntry
=
findBestBgpRoute
(
bgpRouteEntry
.
prefix
());
if
(
bestBgpRouteEntry
!=
null
)
{
// Install the next best route
bgpRoutes
.
put
(
bestBgpRouteEntry
.
prefix
(),
bestBgpRouteEntry
);
routeUpdate
=
new
RouteUpdate
(
RouteUpdate
.
Type
.
UPDATE
,
bestBgpRouteEntry
);
// Forward the result route updates to the Route Listener
routeListener
.
update
(
routeUpdate
);
return
;
}
//
// No route found. Remove the route entry
//
bgpRoutes
.
remove
(
bgpRouteEntry
.
prefix
());
routeUpdate
=
new
RouteUpdate
(
RouteUpdate
.
Type
.
DELETE
,
bgpRouteEntry
);
// Forward the result route updates to the Route Listener
routeListener
.
update
(
routeUpdate
);
}
/**
* Finds the best route entry among all BGP Sessions.
*
* @param prefix the prefix of the route
* @return the best route if found, otherwise null
*/
private
BgpRouteEntry
findBestBgpRoute
(
IpPrefix
prefix
)
{
BgpRouteEntry
bestRoute
=
null
;
// Iterate across all BGP Sessions and select the best route
for
(
BgpSession
bgpSession
:
bgpSessions
.
values
())
{
BgpRouteEntry
route
=
bgpSession
.
findBgpRouteEntry
(
prefix
);
if
(
route
==
null
)
{
continue
;
}
if
((
bestRoute
==
null
)
||
route
.
isBetterThan
(
bestRoute
))
{
bestRoute
=
route
;
}
}
return
bestRoute
;
}
}
}
apps/sdnip/src/main/java/org/onlab/onos/sdnip/bgp/package-info.java
0 → 100644
View file @
ab63aac
/**
* Implementation of the BGP protocol.
*/
package
org
.
onlab
.
onos
.
sdnip
.
bgp
;
\ No newline at end of file
utils/misc/src/main/java/org/onlab/packet/IpAddress.java
View file @
ab63aac
...
...
@@ -2,13 +2,15 @@ package org.onlab.packet;
import
java.util.Arrays
;
/**
* A class representing an IPv4 address.
* <p/>
* TODO this class is a clone of IpPrefix and still needs to be modified to
* look more like an IpAddress.
*/
public
final
class
IpAddress
{
public
final
class
IpAddress
implements
Comparable
<
IpAddress
>
{
// TODO a comparator for netmasks? E.g. for sorting by prefix match order.
...
...
@@ -289,6 +291,13 @@ public final class IpAddress {
}
@Override
public
int
compareTo
(
IpAddress
o
)
{
Long
lv
=
((
long
)
this
.
toRealInt
())
&
0xffffffff
L
;
Long
rv
=
((
long
)
o
.
toRealInt
())
&
0xffffffff
L
;
return
lv
.
compareTo
(
rv
);
}
@Override
public
int
hashCode
()
{
final
int
prime
=
31
;
int
result
=
1
;
...
...
Please
register
or
login
to post a comment