diff options
6 files changed, 133 insertions, 20 deletions
diff --git a/src/main/java/com/lumaserv/bgp/BGPFsm.java b/src/main/java/com/lumaserv/bgp/BGPFsm.java index 7929d36..9bfc30b 100644 --- a/src/main/java/com/lumaserv/bgp/BGPFsm.java +++ b/src/main/java/com/lumaserv/bgp/BGPFsm.java @@ -8,6 +8,7 @@ import lombok.Getter; import lombok.Setter; import java.io.IOException; +import java.util.Optional; import java.util.Timer; import java.util.TimerTask; @@ -277,7 +278,15 @@ public class BGPFsm { @Override void openMsg(BGPOpen msg) { System.out.println("openMsg:" + msg); - if(session.getConfiguration().getRemoteAs() == msg.getAsn()) { + + Optional<BGPOpen.AS4Capability> as4Opt = + msg.getAS4Capability(); + int remoteAsn = session.getConfiguration().getRemoteAs(); + int openAsn = as4Opt.isPresent() + ? as4Opt.get().getAsn() + : msg.getAsn(); + + if(remoteAsn == openAsn) { // - resets the DelayOpenTimer to zero, // - sets the BGP ConnectRetryTimer to zero, session.keepAlive(); @@ -290,8 +299,14 @@ public class BGPFsm { setState(OPEN_CONFIRM); } else { System.out.println("Bad asn"); - // - sends a NOTIFICATION message with the appropriate error code, - // - sets the ConnectRetryTimer to zero, + try { + BGPNotification notification = new BGPNotification() + .setMajorErrorCode(BGPNotification.Error.OPEN_MESSAGE_ERROR.getCode()) + .setMinorErrorCode(BGPNotification.OpenMessageError.BAD_PEER_AS.getCode()); + session.sendNotification(notification); + } catch (IOException ex) { + } + setConnectRetryTimer(0); // - releases all BGP resources, session.dropConnection(); connectRetryCounter++; diff --git a/src/main/java/com/lumaserv/bgp/BGPSession.java b/src/main/java/com/lumaserv/bgp/BGPSession.java index c6a8cd2..bb38ebf 100644 --- a/src/main/java/com/lumaserv/bgp/BGPSession.java +++ b/src/main/java/com/lumaserv/bgp/BGPSession.java @@ -29,6 +29,9 @@ public class BGPSession implements Runnable { boolean outgoing; BGPFsm fsm; Thread thread; + boolean isAdvAS4Capability; + @Getter + boolean isAS4Capability; public BGPSession(Socket socket, BGPSessionConfiguration configuration, BGPFsm fsm) throws IOException { this.configuration = configuration; @@ -75,10 +78,11 @@ public class BGPSession implements Runnable { break; case OPEN: BGPOpen open = new BGPOpen(packet.getMessage()); + isAS4Capability = isAdvAS4Capability && open.getAS4Capability().isPresent(); fsm.getCurrentState().openMsg(open); break; case UPDATE: { - BGPUpdate update = new BGPUpdate(packet.getMessage()); + BGPUpdate update = new BGPUpdate(this, packet.getMessage()); fsm.getCurrentState().updateMsg(update); configuration.getListener().onUpdate(this, update); break; @@ -103,6 +107,7 @@ public class BGPSession implements Runnable { } void sendOpen(BGPOpen request) throws IOException { + isAdvAS4Capability = request.getAS4Capability().isPresent(); outputStream.write(new BGPPacket().setType(BGPPacket.Type.OPEN).setMessage(request.build()).build()); System.out.println("Sent open"); } @@ -111,12 +116,14 @@ System.out.println("Sent open"); BGPOpen.Capabilities caps = new BGPOpen.Capabilities(); caps.getCapabilities().add(new BGPOpen.MultiprotocolExtensionCapability(AFI.IPV4.getValue(), SAFI.UNICAST.getValue())); caps.getCapabilities().add(new BGPOpen.MultiprotocolExtensionCapability(AFI.IPV6.getValue(), SAFI.UNICAST.getValue())); + caps.getCapabilities().add(new BGPOpen.AS4Capability(configuration.getLocalAs())); BGPOpen request = new BGPOpen() - .setAsn(configuration.getLocalAs()) .setHoldTime(BGPFsm.CONFIG_HOLD_TIME) .setVersion(BGPFsm.CONFIG_VERSION) .setIdentifier(configuration.getLocalIdentifier()); + if ((configuration.getLocalAs() & 0xFFFFFFFFL) < 0xFFFF) + request.setAsn(configuration.getLocalAs()); request.getOptionalParameters().add(caps); sendOpen(request); diff --git a/src/main/java/com/lumaserv/bgp/protocol/attribute/ASPathAttribute.java b/src/main/java/com/lumaserv/bgp/protocol/attribute/ASPathAttribute.java index 6405924..c4c6962 100644 --- a/src/main/java/com/lumaserv/bgp/protocol/attribute/ASPathAttribute.java +++ b/src/main/java/com/lumaserv/bgp/protocol/attribute/ASPathAttribute.java @@ -1,5 +1,7 @@ package com.lumaserv.bgp.protocol.attribute; +import com.lumaserv.bgp.BGPSession; + import lombok.Getter; import lombok.Setter; @@ -12,14 +14,27 @@ public class ASPathAttribute implements PathAttribute { List<Segment> segments = new ArrayList<>(); - public ASPathAttribute(byte typeCode, byte[] data) { + public ASPathAttribute(BGPSession session, byte typeCode, byte[] data) { + final boolean isAS4 = session.isAS4Capability(); + final int asnLen = isAS4 ? 4 : 2; int offset = 0; while (offset < data.length) { Segment segment = new Segment().setType(data[offset]); byte length = data[offset + 1]; - for(int i=0; i<length; i++) - segment.asns.add(((data[offset + 2 + (i * 2)] & 0xFF) << 8) | (data[offset + 3 + (i * 2)] & 0xFF)); - offset += 2 + (length * 2); + for(int i=0; i<length; i++) { + int asn; + if (isAS4) { + asn = ((data[offset + 2 + (i * 4)] & 0xFF) << 24) + | ((data[offset + 3 + (i * 4)] & 0xFF) << 16) + | ((data[offset + 4 + (i * 4)] & 0xFF) << 8) + | (data[offset + 5 + (i * 4)] & 0xFF); + } else { + asn = (((data[offset + 2 + (i * 2)] & 0xFF) << 8) + | (data[offset + 3 + (i * 2)] & 0xFF)); + } + segment.asns.add(asn); + } + offset += 2 + (length * asnLen); segments.add(segment); } } @@ -45,7 +60,7 @@ public class ASPathAttribute implements PathAttribute { for(Integer asn : asns) { if (buf.length() > 1) buf.append(" "); - buf.append(asn); + buf.append((long)asn & 0xFFFFFFFFL); } buf.append(")"); return buf.toString(); diff --git a/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java b/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java index 1ba5ce4..06bbed5 100644 --- a/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java +++ b/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java @@ -1,16 +1,18 @@ package com.lumaserv.bgp.protocol.attribute; +import com.lumaserv.bgp.BGPSession; + public interface PathAttribute { byte getTypeCode(); byte[] build(); - static PathAttribute from(byte typeCode, byte[] data) { + static PathAttribute from(BGPSession session, byte typeCode, byte[] data) { switch (typeCode & 0xFF) { case 1: return new OriginAttribute(typeCode, data); case 2: - return new ASPathAttribute(typeCode, data); + return new ASPathAttribute(session, typeCode, data); case 3: return new NextHopAttribute(typeCode, data); case 6: diff --git a/src/main/java/com/lumaserv/bgp/protocol/message/BGPOpen.java b/src/main/java/com/lumaserv/bgp/protocol/message/BGPOpen.java index ae4e32a..4937e1b 100644 --- a/src/main/java/com/lumaserv/bgp/protocol/message/BGPOpen.java +++ b/src/main/java/com/lumaserv/bgp/protocol/message/BGPOpen.java @@ -7,7 +7,9 @@ import lombok.Setter; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; +import java.util.Optional; @Getter @Setter @@ -21,7 +23,7 @@ public class BGPOpen { List<Parameter> optionalParameters = new ArrayList<>(); @Getter - enum ParameterType { + public enum ParameterType { CAPABILITIES(2); byte type; @@ -31,7 +33,7 @@ public class BGPOpen { } } - interface Parameter { + public interface Parameter { byte getType(); int getValue(byte[] dst, int offset); } @@ -77,6 +79,12 @@ public class BGPOpen { return offset - first; } + public Optional<Capability> getCapability(Capability.Code capabilityCode) { + return capabilities.stream() + .filter(c -> c.getCode() == capabilityCode.getCode()) + .findAny(); + } + Capabilities(byte[] src, int offset, int length) { int first = offset; while ((offset - first) < length) { @@ -85,6 +93,8 @@ public class BGPOpen { if (code == Capability.Code.MULTIPROTOCOL_EXTENSION.getCode()) { capabilities.add(new MultiprotocolExtensionCapability(src, offset, capLength)); + } else if (code == Capability.Code.AS4.getCode()) { + capabilities.add(new AS4Capability(src, offset, capLength)); } else { byte[] value = new byte[capLength]; System.arraycopy(src, offset, value, 0, capLength); @@ -112,16 +122,31 @@ public class BGPOpen { } } - interface Capability { + public interface Capability { @Getter enum Code { - MULTIPROTOCOL_EXTENSION(1); + MULTIPROTOCOL_EXTENSION(1), + ROUTE_REFRESH(2), + EXTENDED_NEXT_HOP(5), + GRACEFUL_RESTART(64), + AS4(65), + ENHANCED_ROUTE_REFRESH(70), + LONG_LIVED_GRACEFUL_RESTART(71); byte code; Code(int code) { this.code = (byte)code; } + + public static Code fromInt(int code) { + return EnumSet + .allOf(Code.class) + .stream() + .filter(e -> e.getCode() == code) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("unknown capability: " + code)); + } } byte getCode(); @@ -142,7 +167,7 @@ public class BGPOpen { @Override public String toString() { - return "Unknown{code=" + code + ", length=" + value.length + "}"; + return "Unknown{code=" + Code.fromInt(code) + ", length=" + value.length + "}"; } } @@ -178,8 +203,41 @@ public class BGPOpen { } } + @AllArgsConstructor + @Getter + public static class AS4Capability implements Capability { + int asn; + + @Override + public byte getCode() { return Capability.Code.AS4.getCode(); } + + @Override + public int getValue(byte[] dst, int offset) { + dst[offset] = (byte)((asn >> 24) & 0xFF); + dst[offset + 1] = (byte)((asn >> 16) & 0xFF); + dst[offset + 2] = (byte)((asn >> 8) & 0xFF); + dst[offset + 3] = (byte)(asn & 0xFF); + return 4; + } + + AS4Capability(byte[] src, int offset, int length) { + if (length != 4) { + throw new RuntimeException("Bad AS4Capability"); + } + + asn = ((src[offset] & 0xFF) << 24) + | ((src[offset + 1] & 0xFF) << 16) + | ((src[offset + 2] & 0xFF) << 8) + | (src[offset + 3] & 0xFF); + } + + @Override + public String toString() { + return "as4: " + (asn & 0xFFFFFFFFL); + } + } + public BGPOpen(ByteBuffer message) { -System.out.println("BGPOpen pos:" + message.position()); version = message.get(); asn = message.getShort() & 0xFFFF; holdTime = message.getShort() & 0xFFFF; @@ -210,6 +268,21 @@ System.out.println("BGPOpen pos:" + message.position()); this(ByteBuffer.wrap(message)); } + public Optional<Parameter> getOptionalParameter(ParameterType type) { + return optionalParameters.stream() + .filter(o -> o.getType() == type.getType()) + .findAny(); + } + + public Optional<Capability> getCapability(Capability.Code capabilityCode) { + Capabilities caps = (Capabilities)getOptionalParameter(BGPOpen.ParameterType.CAPABILITIES).orElse(new Capabilities()); + return caps.getCapability(capabilityCode); + } + + public Optional<AS4Capability> getAS4Capability() { + return getCapability(Capability.Code.AS4).map(c -> (AS4Capability)c); + } + public byte[] build() { ByteBuffer buf = ByteBuffer.allocate(256); build(buf); diff --git a/src/main/java/com/lumaserv/bgp/protocol/message/BGPUpdate.java b/src/main/java/com/lumaserv/bgp/protocol/message/BGPUpdate.java index fa23432..f5af399 100644 --- a/src/main/java/com/lumaserv/bgp/protocol/message/BGPUpdate.java +++ b/src/main/java/com/lumaserv/bgp/protocol/message/BGPUpdate.java @@ -1,5 +1,6 @@ package com.lumaserv.bgp.protocol.message; +import com.lumaserv.bgp.BGPSession; import com.lumaserv.bgp.protocol.IPPrefix; import com.lumaserv.bgp.protocol.attribute.PathAttribute; import lombok.Getter; @@ -16,7 +17,7 @@ public class BGPUpdate { List<PathAttribute> attributes = new ArrayList<>(); List<IPPrefix> prefixes = new ArrayList<>(); - public BGPUpdate(byte[] message) { + public BGPUpdate(BGPSession session, byte[] message) { int routesLength = ((message[0] & 0xFF) << 8) | (message[1] & 0xFF); int offset = 2; int offsetOffset = 2; @@ -46,7 +47,7 @@ public class BGPUpdate { byte[] data = new byte[length]; System.arraycopy(message, offset, data, 0, length); offset += length; - attributes.add(PathAttribute.from(typeCode, data)); + attributes.add(PathAttribute.from(session, typeCode, data)); } while (offset < message.length) { IPPrefix prefix = new IPPrefix() |