summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2022-01-30 14:48:17 +0100
committerMikael Magnusson <mikma@users.sourceforge.net>2022-03-12 22:53:22 +0100
commita132bf174f35bc16546ecce4bcfb78e0cf1e9465 (patch)
tree9012acf165e0a9549d7b63fa06921f06b5cafe74 /src/main/java
parent95626768e200b4a124696b5fe983296d86efe82c (diff)
Implement tunnel encaps attribute
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java2
-rw-r--r--src/main/java/com/lumaserv/bgp/protocol/attribute/TunnelEncapsAttribute.java400
2 files changed, 402 insertions, 0 deletions
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 5f4816e..1ba5ce4 100644
--- a/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java
+++ b/src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java
@@ -31,6 +31,8 @@ public interface PathAttribute {
return new AS4AggregatorAttribute(typeCode, data);
case 21:
return new ASPathLimitAttribute(typeCode, data);
+ case 23:
+ return new TunnelEncapsAttribute(data);
case 32:
return new LargeCommunityAttribute(typeCode, data);
case 255:
diff --git a/src/main/java/com/lumaserv/bgp/protocol/attribute/TunnelEncapsAttribute.java b/src/main/java/com/lumaserv/bgp/protocol/attribute/TunnelEncapsAttribute.java
new file mode 100644
index 0000000..747b923
--- /dev/null
+++ b/src/main/java/com/lumaserv/bgp/protocol/attribute/TunnelEncapsAttribute.java
@@ -0,0 +1,400 @@
+package com.lumaserv.bgp.protocol.attribute;
+
+import com.lumaserv.bgp.protocol.AFI;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class TunnelEncapsAttribute implements PathAttribute {
+
+ List<Tunnel> tunnels = new ArrayList<>();
+
+ @Getter
+ public static class Tunnel {
+ int type;
+
+ List<SubTlv> subTlvs = new ArrayList<>();
+
+ Tunnel(int type, byte[] src, int offset, int length) {
+ this.type = type;
+ int end = offset + length;
+ // System.out.println("Tunnel offset:" + offset + " length:" + length + " end:" + end);
+ while (offset < end) {
+ // System.out.println("Tunnel offset:" + offset);
+
+ int subType = src[offset++] & 0xFF;
+ int subLen = 0;
+ if (subType < 128) {
+ subLen = src[offset++] & 0xFF;
+ } else {
+ subLen = (src[offset++] & 0xFF) << 8 | (src[offset++] & 0xFF);
+ }
+
+ switch (subType) {
+ case Encapsulation.TYPE:
+ switch (type) {
+ case 51820:
+ subTlvs.add(new WireGuard(src, offset, subLen));
+ break;
+ default:
+ subTlvs.add(new Encapsulation(src, offset, subLen));
+ break;
+ }
+ break;
+ case Color.TYPE:
+ subTlvs.add(new Color(src, offset, subLen));
+ break;
+ case EgressEndpoint.TYPE:
+ subTlvs.add(new EgressEndpoint(src, offset, subLen));
+ break;
+ case UDPDestinationPort.TYPE:
+ subTlvs.add(new UDPDestinationPort(src, offset, subLen));
+ break;
+ default:
+ subTlvs.add(new UnknownSubTlv(subType, src, offset, subLen));
+ }
+ offset += subLen;
+ }
+ }
+
+ int build(byte[] dst, int offset) {
+ int first = offset;
+
+ for (SubTlv subTlv: subTlvs) {
+ dst[offset++] = (byte)subTlv.getType();
+
+ if (subTlv.getType() < 128) {
+ int length = subTlv.getValue(dst, offset + 1);
+ dst[offset] = (byte)(length & 0xFF);
+ offset += length;
+ } else {
+ int length = subTlv.getValue(dst, offset + 2);
+ dst[offset] = (byte)((length >> 8) & 0xFF);
+ dst[offset] = (byte)(length & 0xFF);
+ offset += length;
+ }
+ }
+
+ return offset - first;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("{type:");
+ buf.append(type);
+ buf.append(", ");
+ boolean first = true;
+ for (SubTlv subTlv: subTlvs) {
+ if (!first) {
+ buf.append(", ");
+ }
+ buf.append(subTlv);
+ first = false;
+ }
+ buf.append("}");
+
+ return buf.toString();
+ }
+ }
+
+ interface SubTlv {
+ int getType();
+ int getValue(byte[] dst, int offset);
+ }
+
+ @Getter
+ public static class Encapsulation implements SubTlv {
+ int offset;
+ int length;
+ byte[] array;
+
+ public static final int TYPE = 1;
+
+ Encapsulation(byte[] src, int offset, int length) {
+ this.array = src;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ @Override
+ public int getType() { return TYPE; }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ System.arraycopy(array, this.offset, dst, offset, length);
+ return length;
+ }
+
+ @Override
+ public String toString() {
+ return "{encap length:" + length + "}";
+ }
+ }
+
+ @Getter
+ public static class WireGuard implements SubTlv {
+ byte[] publicKey;
+
+ public static final int TYPE = Encapsulation.TYPE;
+
+ WireGuard(byte[] src, int offset, int length) {
+ if (length != 32) {
+ throw new RuntimeException("WireGuard: bad length");
+ }
+ publicKey = new byte[32];
+ System.arraycopy(src, offset, publicKey, 0, 32);
+ }
+
+ @Override
+ public int getType() { return TYPE; }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ System.arraycopy(publicKey, 0, dst, offset, 32);
+ return 32;
+ }
+
+ @Override
+ public String toString() {
+ return "WireGuard:" + Base64.getEncoder().encodeToString(publicKey);
+ }
+ }
+
+ @Getter
+ public static class Color implements SubTlv {
+ int color;
+ int flags;
+
+ public static final int TYPE = 4;
+
+ Color(byte[] src, int offset, int length) {
+ if (length != 8) {
+ throw new RuntimeException("Color: bad length");
+ }
+ int typeHigh = src[offset++];
+ int typeLow = src[offset++];
+
+ if (typeHigh != 0x03 || typeLow != 0xb) {
+ throw new RuntimeException("Color: bad type");
+ }
+ flags = ((src[offset++] & 0xFF) << 8) |
+ (src[offset++] & 0xFF);
+ color = ((src[offset++] & 0xFF) << 24) |
+ ((src[offset++] & 0xFF) << 16) |
+ ((src[offset++] & 0xFF) << 8) |
+ (src[offset++] & 0xFF);
+ }
+
+ @Override
+ public int getType() { return TYPE; }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ dst[offset++] = (byte)(0x03);
+ dst[offset++] = (byte)(0x0b);
+ dst[offset++] = (byte)((flags >> 8) & 0xFF);
+ dst[offset++] = (byte)(flags & 0xFF);
+ dst[offset++] = (byte)((color >> 24) & 0xFF);
+ dst[offset++] = (byte)((color >> 16) & 0xFF);
+ dst[offset++] = (byte)((color >> 8) & 0xFF);
+ dst[offset++] = (byte)(color & 0xFF);
+ return 8;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("Color:");
+ buf.append(color);
+ if (flags != 0) {
+ buf.append(" (flags 0x");
+ buf.append(Integer.toHexString(flags));
+ buf.append(")");
+ }
+ return buf.toString();
+ }
+ }
+
+ @Getter
+ public static class EgressEndpoint implements SubTlv {
+ InetAddress address;
+
+ public static final int TYPE = 6;
+
+ EgressEndpoint(byte[] src, int offset, int length) {
+ if (length != 6 && length != 10 && length != 22) {
+ throw new RuntimeException("EgressEndpoint: bad length");
+ }
+
+ offset += 4;
+ int family = ((src[offset++] & 0xFF) << 8) | (src[offset++] & 0xFF);
+
+ if (!(family == 0 && length == 6 ||
+ family == AFI.IPV4.getValue() && length == 10 ||
+ family == AFI.IPV6.getValue() && length == 22)) {
+ throw new RuntimeException("EgressEndpoint: bad family or length");
+ }
+
+ byte[] raw = new byte[length - 6];
+ System.arraycopy(src, offset, raw, 0, length - 6);
+ try {
+ address = InetAddress.getByAddress(raw);
+ } catch (UnknownHostException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public int getType() { return TYPE; }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ dst[offset++] = (byte)(0);
+ dst[offset++] = (byte)(0);
+ dst[offset++] = (byte)(0);
+ dst[offset++] = (byte)(0);
+ int family = 0;
+ if (address != null) {
+ if (address instanceof Inet4Address) {
+ family = AFI.IPV4.getValue();
+ } else if (address instanceof Inet6Address) {
+ family = AFI.IPV6.getValue();
+ }
+ }
+ dst[offset++] = (byte)((family >> 8) & 0xFF);
+ dst[offset++] = (byte)(family & 0xFF);
+ int length;
+ if (family != 0) {
+ byte[] raw = address.getAddress();
+ System.arraycopy(raw, 0, dst, offset + 6, raw.length);
+ length = raw.length + 6;
+ } else {
+ length = 6;
+ }
+ return length;
+ }
+
+ @Override
+ public String toString() {
+ return "IP:" + address.getHostAddress();
+ }
+ }
+
+ @Getter
+ public static class UDPDestinationPort implements SubTlv {
+ int port;
+
+ public static final int TYPE = 8;
+
+ UDPDestinationPort(byte[] src, int offset, int length) {
+ if (length != 2) {
+ throw new RuntimeException("UDPDestinationPort: bad length");
+ }
+ port = ((src[offset++] & 0xFF) << 8) | (src[offset++] & 0xFF);
+ }
+
+ @Override
+ public int getType() { return TYPE; }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ dst[offset++] = (byte)((port >> 8) & 0xFF);
+ dst[offset++] = (byte)(port & 0xFF);
+ return 2;
+ }
+
+ @Override
+ public String toString() {
+ return "UDP port:" + port;
+ }
+ }
+
+ @Getter
+ public static class UnknownSubTlv implements SubTlv {
+ int type;
+ int offset;
+ int length;
+ byte[] array;
+
+ UnknownSubTlv(int type, byte[]src, int offset, int length) {
+ this.type = type;
+ this.array = src;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ @Override
+ public int getValue(byte[] dst, int offset) {
+ System.arraycopy(array, this.offset, dst, offset, length);
+ return length;
+ }
+
+ @Override
+ public String toString() {
+ return "{subType:" + type + ", length:" + length + "}";
+ }
+ }
+
+ TunnelEncapsAttribute(byte[] src) {
+ int offset = 0;
+
+ // System.out.println("TunnelEncapsAttribute length:" + src.length);
+ while (offset < src.length) {
+ int type = ((src[offset++] & 0xFF) << 8) | (src[offset++] & 0xFF);
+ int length = ((src[offset++] & 0xFF) << 8) | (src[offset++] & 0xFF);
+ // System.out.println("tlv type:" + type + " length:" + length + " offset:" + offset);
+ tunnels.add(new Tunnel(type, src, offset, length));
+ offset += length;
+ }
+ }
+
+ public byte getTypeCode() { return 23; }
+
+ public byte[] build() {
+ byte[] buf = new byte[256]; // FIXME calculate length
+
+ int offset = 0;
+ for (Tunnel tunnel: tunnels) {
+ buf[offset++] = (byte)tunnel.getType();
+ int lengthPos = offset;
+ offset += 2;
+ int length = tunnel.build(buf, offset);
+ buf[lengthPos] = (byte)((length >> 8) & 0xFF);
+ buf[lengthPos + 1] = (byte)(length & 0xFF);
+ offset += length;
+ }
+
+ return buf;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ boolean first = true;
+ buf.append("[");
+ for (Tunnel tunnel: tunnels) {
+ if (!first)
+ buf.append(" ,");
+ buf.append(tunnel);
+ }
+ buf.append("]");
+
+ return buf.toString();
+ }
+}