| /* |
| * Copyright 2015-present 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; |
| private 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(); |
| |
| Short withDrwLen = cb.readShort(); |
| |
| if (cb.readableBytes() < withDrwLen) { |
| Validation.validateLen(BgpErrorType.UPDATE_MESSAGE_ERROR, |
| BgpErrorType.MALFORMED_ATTRIBUTE_LIST, |
| cb.readableBytes()); |
| } |
| log.debug("Reading withdrawn routes length"); |
| ChannelBuffer tempCb = cb.readBytes(withDrwLen); |
| if (withDrwLen != 0) { |
| // Parsing WithdrawnRoutes |
| withDrwRoutes = parseWithdrawnRoutes(tempCb); |
| log.debug("Withdrawn routes parsed"); |
| } |
| 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); |
| } |
| log.debug("Total path attribute length read"); |
| 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) || (afi == Constants.AFI_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(); |
| } |
| } |