blob: e2bf2183f4aa78efce52cf726c9c3138e4314b58 [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.bgpio.protocol.ver4;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.jboss.netty.buffer.ChannelBuffer;
import org.onlab.packet.IpPrefix;
import org.onosproject.bgpio.exceptions.BgpParseException;
import org.onosproject.bgpio.protocol.BgpMessageReader;
import org.onosproject.bgpio.protocol.BgpMessageWriter;
import org.onosproject.bgpio.protocol.BgpType;
import org.onosproject.bgpio.protocol.BgpUpdateMsg;
import org.onosproject.bgpio.protocol.BgpVersion;
import org.onosproject.bgpio.types.BgpErrorType;
import org.onosproject.bgpio.types.BgpHeader;
import org.onosproject.bgpio.types.MpReachNlri;
import org.onosproject.bgpio.types.MpUnReachNlri;
import org.onosproject.bgpio.util.Constants;
import org.onosproject.bgpio.util.Validation;
import org.onosproject.bgpio.types.BgpValueType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.MoreObjects;
/**
* BGP Update Message: UPDATE messages are used to transfer routing information
* between BGP peers. The information in the UPDATE message is used by core to
* construct a graph
*/
public class BgpUpdateMsgVer4 implements BgpUpdateMsg {
/* 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ +
| Marker |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length | Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Withdrawn Routes Length (2 octets) |
+-----------------------------------------------------+
| Withdrawn Routes (variable) |
+-----------------------------------------------------+
| Total Path Attribute Length (2 octets) |
+-----------------------------------------------------+
| Path Attributes (variable) |
+-----------------------------------------------------+
| Network Layer Reachability Information (variable) |
+-----------------------------------------------------+
REFERENCE : RFC 4271
*/
protected static final Logger log = LoggerFactory
.getLogger(BgpUpdateMsgVer4.class);
public static final byte PACKET_VERSION = 4;
//Withdrawn Routes Length(2) + Total Path Attribute Length(2)
public static final int PACKET_MINIMUM_LENGTH = 4;
public static final int MARKER_LENGTH = 16;
public static final int BYTE_IN_BITS = 8;
public static final int MIN_LEN_AFTER_WITHDRW_ROUTES = 2;
public static final int MINIMUM_COMMON_HEADER_LENGTH = 19;
public static final BgpType MSG_TYPE = BgpType.UPDATE;
public static byte[] marker = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
public static final BgpHeader DEFAULT_UPDATE_HEADER = new BgpHeader(marker,
(short) PACKET_MINIMUM_LENGTH, (byte) 0X02);
public static final BgpUpdateMsgVer4.Reader READER = new Reader();
private List<IpPrefix> withdrawnRoutes;
private BgpPathAttributes bgpPathAttributes;
private BgpHeader bgpHeader;
private List<IpPrefix> nlri;
/**
* Constructor to initialize parameters for BGP Update message.
*
* @param bgpHeader in Update message
* @param withdrawnRoutes withdrawn routes
* @param bgpPathAttributes BGP Path attributes
* @param nlri Network Layer Reachability Information
*/
public BgpUpdateMsgVer4(BgpHeader bgpHeader, List<IpPrefix> withdrawnRoutes,
BgpPathAttributes bgpPathAttributes, List<IpPrefix> nlri) {
this.bgpHeader = bgpHeader;
this.withdrawnRoutes = withdrawnRoutes;
this.bgpPathAttributes = bgpPathAttributes;
this.nlri = nlri;
}
/**
* Reader reads BGP Update Message from the channel buffer.
*/
static class Reader implements BgpMessageReader<BgpUpdateMsg> {
@Override
public BgpUpdateMsg readFrom(ChannelBuffer cb, BgpHeader bgpHeader)
throws BgpParseException {
if (cb.readableBytes() != (bgpHeader.getLength() - MINIMUM_COMMON_HEADER_LENGTH)) {
Validation.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.BAD_MESSAGE_LENGTH, bgpHeader.getLength());
}
LinkedList<IpPrefix> withDrwRoutes = new LinkedList<>();
LinkedList<IpPrefix> nlri = new LinkedList<>();
BgpPathAttributes bgpPathAttributes = new BgpPathAttributes();
// Reading Withdrawn Routes Length
Short withDrwLen = cb.readShort();
if (cb.readableBytes() < withDrwLen) {
Validation.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST,
cb.readableBytes());
}
ChannelBuffer tempCb = cb.readBytes(withDrwLen);
if (withDrwLen != 0) {
// Parsing WithdrawnRoutes
withDrwRoutes = parseWithdrawnRoutes(tempCb);
}
if (cb.readableBytes() < MIN_LEN_AFTER_WITHDRW_ROUTES) {
log.debug("Bgp Path Attribute len field not present");
throw new BgpParseException(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST, null);
}
// Reading Total Path Attribute Length
short totPathAttrLen = cb.readShort();
int len = withDrwLen + totPathAttrLen + PACKET_MINIMUM_LENGTH;
if (len > bgpHeader.getLength()) {
throw new BgpParseException(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST, null);
}
if (totPathAttrLen != 0) {
// Parsing BGPPathAttributes
if (cb.readableBytes() < totPathAttrLen) {
Validation
.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST,
cb.readableBytes());
}
tempCb = cb.readBytes(totPathAttrLen);
bgpPathAttributes = BgpPathAttributes.read(tempCb);
}
if (cb.readableBytes() > 0) {
// Parsing NLRI
nlri = parseNlri(cb);
}
return new BgpUpdateMsgVer4(bgpHeader, withDrwRoutes,
bgpPathAttributes, nlri);
}
}
/**
* Builder class for BGP update message.
*/
static class Builder implements BgpUpdateMsg.Builder {
BgpHeader bgpMsgHeader = null;
BgpPathAttributes bgpPathAttributes;
List<IpPrefix> withdrawnRoutes;
List<IpPrefix> nlri;
@Override
public BgpUpdateMsg build() {
BgpHeader bgpMsgHeader = DEFAULT_UPDATE_HEADER;
return new BgpUpdateMsgVer4(bgpMsgHeader, withdrawnRoutes, bgpPathAttributes, nlri);
}
@Override
public Builder setHeader(BgpHeader bgpMsgHeader) {
this.bgpMsgHeader = bgpMsgHeader;
return this;
}
@Override
public Builder setBgpPathAttributes(List<BgpValueType> attributes) {
this.bgpPathAttributes = new BgpPathAttributes(attributes);
return this;
}
}
public static final Writer WRITER = new Writer();
/**
* Writer class for writing BGP update message to channel buffer.
*/
public static class Writer implements BgpMessageWriter<BgpUpdateMsgVer4> {
@Override
public void write(ChannelBuffer cb, BgpUpdateMsgVer4 message) throws BgpParseException {
int startIndex = cb.writerIndex();
short afi = 0;
byte safi = 0;
// write common header and get msg length index
int msgLenIndex = message.bgpHeader.write(cb);
if (msgLenIndex <= 0) {
throw new BgpParseException("Unable to write message header.");
}
List<BgpValueType> pathAttr = message.bgpPathAttributes.pathAttributes();
if (pathAttr != null) {
Iterator<BgpValueType> listIterator = pathAttr.iterator();
while (listIterator.hasNext()) {
BgpValueType attr = listIterator.next();
if (attr instanceof MpReachNlri) {
MpReachNlri mpReach = (MpReachNlri) attr;
afi = mpReach.afi();
safi = mpReach.safi();
} else if (attr instanceof MpUnReachNlri) {
MpUnReachNlri mpUnReach = (MpUnReachNlri) attr;
afi = mpUnReach.afi();
safi = mpUnReach.safi();
}
if ((afi == Constants.AFI_FLOWSPEC_VALUE) && ((safi == Constants.SAFI_FLOWSPEC_VALUE)
|| (safi == Constants.VPN_SAFI_FLOWSPEC_VALUE))) {
//unfeasible route length
cb.writeShort(0);
}
}
}
if (message.bgpPathAttributes != null) {
message.bgpPathAttributes.write(cb);
}
// write UPDATE Object Length
int length = cb.writerIndex() - startIndex;
cb.setShort(msgLenIndex, (short) length);
message.bgpHeader.setLength((short) length);
}
}
/**
* Parses NLRI from channel buffer.
*
* @param cb channelBuffer
* @return list of IP Prefix
* @throws BgpParseException while parsing NLRI
*/
public static LinkedList<IpPrefix> parseNlri(ChannelBuffer cb)
throws BgpParseException {
LinkedList<IpPrefix> nlri = new LinkedList<>();
while (cb.readableBytes() > 0) {
int length = cb.readByte();
IpPrefix ipPrefix;
if (length == 0) {
byte[] prefix = new byte[] {0};
ipPrefix = Validation.bytesToPrefix(prefix, length);
nlri.add(ipPrefix);
} else {
int len = length / BYTE_IN_BITS;
int reminder = length % BYTE_IN_BITS;
if (reminder > 0) {
len = len + 1;
}
if (cb.readableBytes() < len) {
Validation.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST,
cb.readableBytes());
}
byte[] prefix = new byte[len];
cb.readBytes(prefix, 0, len);
ipPrefix = Validation.bytesToPrefix(prefix, length);
nlri.add(ipPrefix);
}
}
return nlri;
}
/**
* Parsing withdrawn routes from channel buffer.
*
* @param cb channelBuffer
* @return list of IP prefix
* @throws BgpParseException while parsing withdrawn routes
*/
public static LinkedList<IpPrefix> parseWithdrawnRoutes(ChannelBuffer cb)
throws BgpParseException {
LinkedList<IpPrefix> withDrwRoutes = new LinkedList<>();
while (cb.readableBytes() > 0) {
int length = cb.readByte();
IpPrefix ipPrefix;
if (length == 0) {
byte[] prefix = new byte[] {0};
ipPrefix = Validation.bytesToPrefix(prefix, length);
withDrwRoutes.add(ipPrefix);
} else {
int len = length / BYTE_IN_BITS;
int reminder = length % BYTE_IN_BITS;
if (reminder > 0) {
len = len + 1;
}
if (cb.readableBytes() < len) {
Validation
.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR,
BgpErrorType.MALFORMED_ATTRIBUTE_LIST,
cb.readableBytes());
}
byte[] prefix = new byte[len];
cb.readBytes(prefix, 0, len);
ipPrefix = Validation.bytesToPrefix(prefix, length);
withDrwRoutes.add(ipPrefix);
}
}
return withDrwRoutes;
}
@Override
public BgpVersion getVersion() {
return BgpVersion.BGP_4;
}
@Override
public BgpType getType() {
return BgpType.UPDATE;
}
@Override
public void writeTo(ChannelBuffer channelBuffer) {
try {
WRITER.write(channelBuffer, this);
} catch (BgpParseException e) {
log.debug("[writeTo] Error: " + e.toString());
}
}
@Override
public BgpPathAttributes bgpPathAttributes() {
return this.bgpPathAttributes;
}
@Override
public List<IpPrefix> withdrawnRoutes() {
return withdrawnRoutes;
}
@Override
public List<IpPrefix> nlri() {
return nlri;
}
@Override
public BgpHeader getHeader() {
return this.bgpHeader;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.omitNullValues()
.add("bgpHeader", bgpHeader)
.add("withDrawnRoutes", withdrawnRoutes)
.add("nlri", nlri)
.add("bgpPathAttributes", bgpPathAttributes)
.toString();
}
}