diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2022-01-30 14:48:17 +0100 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2022-03-12 22:53:22 +0100 |
commit | a132bf174f35bc16546ecce4bcfb78e0cf1e9465 (patch) | |
tree | 9012acf165e0a9549d7b63fa06921f06b5cafe74 /src/main/java | |
parent | 95626768e200b4a124696b5fe983296d86efe82c (diff) |
Implement tunnel encaps attribute
Diffstat (limited to 'src/main/java')
-rw-r--r-- | src/main/java/com/lumaserv/bgp/protocol/attribute/PathAttribute.java | 2 | ||||
-rw-r--r-- | src/main/java/com/lumaserv/bgp/protocol/attribute/TunnelEncapsAttribute.java | 400 |
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(); + } +} |