diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index fb8e9e0..88c338b --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,14 @@ local.properties # Eclipse project files .classpath .project + +*.iml +*.ipr +*.iws +.idea +target +atlassian-ide-plugin.xml +.settings +.DS_Store + +*.swp diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dff5f3a --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 130b380..b846904 --- a/README.md +++ b/README.md @@ -3,7 +3,4 @@ java-mrt Java library to parse the binary MRT format. -I have used this library over the last years extensively. -Please send any bug fixes to me paaguti :#at#: gmail _dot_ com - This library is released under LGPL license. Read LICENSE.txt. diff --git a/build.xml b/build.xml old mode 100644 new mode 100755 index 933c96a..e1496fe --- a/build.xml +++ b/build.xml @@ -1,10 +1,10 @@ - - - + + + - + diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..f12e1e4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + net.apnic.mrt + mrt + 0.0.3-SNAPSHOT + jar + + Java MRT File Dumper + Java Multi-Threaded Routing Toolkit File Dumper + https://github.com/APNIC-net/java-mrt + + + >https://github.com/APNIC-net/java-mrt + scm:git:git@github.com:APNIC-net/java-mrt.git + scm:git:git@github.com:APNIC-net/java-mrt.git + + + + + nexus-staging + Nexus Staging Repository + http://nexus.apnic.net:8081/nexus/content/repositories/staging + + + nexus-snapshots + Nexus Snapshot Repository + http://nexus.apnic.net:8081/nexus/content/repositories/snapshots + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + + + + + org.testng + testng + 6.8.1 + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.javamrt.progs.route_btoa + + + + + + + diff --git a/src/org/javamrt/mrt/AS.java b/src/main/java/org/javamrt/mrt/AS.java similarity index 57% rename from src/org/javamrt/mrt/AS.java rename to src/main/java/org/javamrt/mrt/AS.java index 8f05418..b834e74 100644 --- a/src/org/javamrt/mrt/AS.java +++ b/src/main/java/org/javamrt/mrt/AS.java @@ -6,7 +6,10 @@ package org.javamrt.mrt; +import java.math.BigInteger; +import java.util.Collections; import java.util.Comparator; +import java.util.List; /** * @author paag @@ -16,70 +19,36 @@ public class AS implements Comparable, Comparator { public static final int AS_TRANS = 23456; public static final AS NullAS = new AS(0); - // protected long ASnumber; - protected byte ASCode[] = {0,0,0,0}; + private long asNumber; protected AS() { } public AS(long ASnumber) { - setASCode(ASnumber & 0xffffffff); + asNumber = ASnumber & 0xffffffffL; } - public AS(byte[] as) throws Exception { - setASCode(as); - } - - - private void setASCode(long val) { - for (int i = 0; i < 4; i++) - ASCode[i] = (byte) ((val >> ((3 - i) * 8)) & 0xff); - } - - private void setASCode(byte[] newval) throws Exception { - if (newval.length == 2) { - this.ASCode[0] = - this.ASCode[1] = 0; - this.ASCode[2] = newval[0]; - this.ASCode[3] = newval[1]; - } else if (newval.length == 4) { - this.ASCode[0] = newval[0]; - this.ASCode[1] = newval[1]; - this.ASCode[2] = newval[2]; - this.ASCode[3] = newval[3]; - } else - throw new Exception(String.format("AS must be 2 or 4 bytes long (%d not allowed)",newval.length)); + public AS(byte[] as) { + if (as.length != 2 && as.length != 4) { + throw new IllegalArgumentException(String.format("AS must be 2 or 4 bytes long (%d not allowed)", as.length)); + } + asNumber = new BigInteger(1, as).longValue(); } public long getASN() { - long result = 0; - for (int i=0; i 0xFFFFL; } /** - * @return true if the AS number is 23456, the 4 byte AS place holder - * comparison is byte by byte + * @return true if the AS number is 23456 */ public boolean isPlaceholder() { - return this.equals(AS_TRANS); + return asNumber == AS_TRANS; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { - if (this.equals(0)) return "AS_NULL"; - if (this.equals(AS_TRANS)) return "AS_TRANS"; - if (this.is4Byte()) return String.format("%d.%d",hiWord(),loWord()); - return String.format("%d", loWord()); - } +// this has never worked, so do not think we should fix it now +// if (this.equals(0)) return "AS_NULL"; +// if (this.equals(AS_TRANS)) return "AS_TRANS"; - private int hiWord() { - return ((this.ASCode[0] & 0xff) << 8) | (this.ASCode[1] & 0xff); + return String.valueOf(asNumber); } - private int loWord() { - return ((this.ASCode[2] & 0xff) << 8) | (this.ASCode[3] & 0xff); - } /** * @param prefix * @return a String containing the prefix followed by the AS textual representation @@ -173,11 +137,22 @@ public static AS parseString(String asspec) throws Exception { asnum = asnum * 0x10000L + asnum1; result = new AS(asnum); } else { - result = new AS(Long.parseLong(asspec)); + long number = Long.parseLong(asspec); + if (number < 0 || number > 0xFFFFFFFFL) { + throw new IllegalArgumentException("number must be in [0,4294967295]"); + } + result = new AS(number); } } else { throw new Exception ("Incorrect AS specification: "+asspec); } return result; } + + /** + * @return list of ASNs, especially useful when not interested in whether it is some type of set, but just want to deal with the numbers + */ + public List getASList() { + return Collections.singletonList(this); + } } diff --git a/src/main/java/org/javamrt/mrt/ASConfedSequence.java b/src/main/java/org/javamrt/mrt/ASConfedSequence.java new file mode 100644 index 0000000..5e36cb4 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/ASConfedSequence.java @@ -0,0 +1,30 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import java.util.List; +import java.util.LinkedList; +import java.util.stream.Collectors; + +public class ASConfedSequence extends AS { + LinkedList asList; + + public ASConfedSequence(List asList) { + this.asList = new LinkedList<>(); + this.asList.addAll(asList); + } + + @Override + public String toString() { + return asList.stream().map(AS::toString).collect(Collectors.joining(" ", "[", "]")); + } + + @Override + public List getASList() { + return this.asList; + } +} diff --git a/src/main/java/org/javamrt/mrt/ASConfedSet.java b/src/main/java/org/javamrt/mrt/ASConfedSet.java new file mode 100644 index 0000000..e0b1b1d --- /dev/null +++ b/src/main/java/org/javamrt/mrt/ASConfedSet.java @@ -0,0 +1,32 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class ASConfedSet extends AS { + + private LinkedList asList; + + public ASConfedSet(final List asList) { + this.asList = new LinkedList<>(); + this.asList.addAll(asList); + } + + @Override + public String toString() { + return asList.stream().map(AS::toString).collect(Collectors.joining(" ", "{", "}")); + } + + @Override + public List getASList() { + return this.asList; + } +} + diff --git a/src/main/java/org/javamrt/mrt/ASPath.java b/src/main/java/org/javamrt/mrt/ASPath.java new file mode 100755 index 0000000..cdf02e1 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/ASPath.java @@ -0,0 +1,412 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import org.javamrt.utils.RecordAccess; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Vector; +import java.util.function.BiConsumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + + +/** + * ASPath is a linked list of elements which are either
+ * AS, ASSet or ASConfedSet + *

+ * version 3.00: complete rewrite with ASPathSegment
+ * + * @author paag + * @version 3.00 + */ +public class ASPath implements Attribute { + + private enum StringParser { + SET("(", ")", (acc, elements) -> acc.add(new ASSet(elements))), + CONFED_SET("{", "}", (acc, elements) -> acc.add(new ASConfedSet(elements))), + CONFED_SEQ("[", "]", (acc, elements) -> acc.add(new ASConfedSequence(elements))), + AS("", "", List::addAll); + + private final static Pattern PATH_ELEMENT_FINDER; + private final static Pattern AS_NUMBER_FINDER; + + static { + final List expressionStrings = Arrays + .stream(StringParser.values()) + .map(parseInfo -> String.format("%s((?:\\s?\\d\\s?)+)%s", parseInfo.start, parseInfo.end)) + .collect(Collectors.toList()); + + PATH_ELEMENT_FINDER = Pattern.compile(String.join("|", expressionStrings)); + AS_NUMBER_FINDER = Pattern.compile("\\d+"); + } + + private final String start; + private final String end; + private final BiConsumer, LinkedList> appender; + + StringParser(final String start, final String end, BiConsumer, LinkedList> appender) { + this.start = Pattern.quote(start); + this.end = Pattern.quote(end); + this.appender = appender; + } + + static List parse(final String input) { + final List result = new LinkedList<>(); + final Matcher pathElementMatcher = PATH_ELEMENT_FINDER.matcher(input); + while (pathElementMatcher.find()) { + for (final StringParser parser : StringParser.values()) { + final String group = pathElementMatcher.group(1 + parser.ordinal()); + if (group != null) { + final LinkedList elements = new LinkedList<>(); + final Matcher asMatcher = AS_NUMBER_FINDER.matcher(group); + while (asMatcher.find()) { + elements.add(new AS(Long.parseLong(asMatcher.group()))); + } + + parser.appender.accept(result, elements); + } + } + } + + return result; + } + } + + protected LinkedList path; + + ASPath() { + this.path = new LinkedList<>(); + this.prependers = null; + } + + ASPath(final List path) { + this.path = new LinkedList<>(); + this.path.addAll(path); + } + +// ASPath(byte[] buffer) throws Exception { +// decode(buffer, 2); +// } + + ASPath(byte[] buffer, int asSize) throws Exception { + decode(buffer, asSize); + } + + + private void decode(byte[] buffer, int asSize) throws Exception { + this.path = new LinkedList(); + int offset = 0; + // route_btoa.System_err_println(String.format("New ASPATH (%d bytes)",buffer.length)); + while (offset < buffer.length) { + ASPathSegment segment = new ASPathSegment(buffer, offset, asSize); + // route_btoa.System_err_println(String.format(" segment @%-2d [t %2d]: %s\n",offset,segment.bType(),segment.toString())); + switch (segment.bType()) { + case MRTConstants.asSequence: + this.path.addAll(segment.getASList()); + break; + case MRTConstants.asSet: + if (null != segment.getAS()) { + this.path.add(segment.getAS()); + } else { + this.path.add(new ASSet(segment.getASList())); + } + break; + case MRTConstants.asConfedSequence: + this.add(new ASConfedSequence(segment.getASList())); + break; + case MRTConstants.asConfedSet: + this.add(new ASConfedSet(segment.getASList())); + break; + default: + RecordAccess.dump(buffer); + throw new Exception( + String.format("Unknown ASPATH Segment Type = %d Len = %d", + segment.bType(), segment.bLen()) + ); + } + offset += segment.bLen(); + } + // + // and now try to see if we have AS PATH Prepend + // + mkPrependers(); + // route_btoa.System_err_println("New ASPATH:"+path.toString()); + } + + public AS get(int i) { + try { + return path.get(i); + } catch (IndexOutOfBoundsException iobe) { + return null; + } + } + + public void set(int index, AS element) { + path.set(index, element); + } + + /** + * Append an AS to the ASPATH + * + * @param as + */ + public void append(AS as) { + add(as); + } + + /** + * alias of append(AS as) + * + * @param as + */ + public void add(AS as) { + path.add(as); + refreshPrependers(as); + } + + public LinkedList getPath() { + return this.path; + } + + /** + * @return the number of hops of the ASPATH + */ + public int length() { + return path.size(); + } + + /** + * @return a Vector with the AS's which are doing prepending. + * @author paag + */ + public Vector getPrependers() { + return prependers; + } + + /** + * @return + */ + public boolean hasAsPathPrepend() { + return prependers != null; + } + + /** + * @author paag + * rebuild the PREPENDER list + *
Called from decode() or when the AS4PATH attribute is decoded + */ + public void mkPrependers() { + int asPathLen; + + this.prependers = null; + if ((asPathLen = this.path.size()) == 0) { + return; + } + for (int i = 0; i < asPathLen; i++) { + AS ahora = this.path.get(i); + if (i != this.path.lastIndexOf(ahora)) { + if (prependers == null) { + prependers = new Vector(); + } + prependers.add(ahora); + i = this.path.lastIndexOf(ahora); + } + } + } + + /** + * refresh the prependers list when an AS is appended to the ASPATH + * + * @param ultimo the AS which was appended to the ASPATH + * @author paag + */ + private void refreshPrependers(AS ultimo) { + // + // is the argument involved in AS PATH prepending? + // + if (this.path.indexOf(ultimo) == this.path.lastIndexOf(ultimo)) { + return; + } + try { + // + // did we already register AS PATH prepending for the argument? + // + if (prependers.contains(ultimo)) { + return; + } + } catch (Exception e) { + // + // no prependers yet + // + prependers = new Vector(); + } + prependers.add(ultimo); + } + + /** + * @param as: an AS + * @return the index of the first occurrence of AS in the ASPATH + */ + public int indexOf(AS as) { + return this.path.indexOf(as); + } + + /** + * @param as: an AS + * @return the index of the last occurrence of AS in the ASPATH + */ + public int lastIndexOf(AS as) { + return this.path.lastIndexOf(as); + } + + public boolean contains(AS as) { + return this.path.indexOf(as) != -1; + } + + /** + * @return a textual representation of the ASPATH + * @author paag + */ + public String toString() { + return asString(this.path); + } + + public static String asString(final List path) { + try { + return path.stream().map(AS::toString).collect(Collectors.joining(" ")); + } catch (Exception e) { + return ""; + } + } + + /** + * Construct an ASPath from the textual representation + * + * @param input The input to parse. + * @return The parsed ASPath + */ + public static ASPath fromString(final String input) { + return new ASPath(StringParser.parse(input)); + } + + /** + * Shortcut for equals(Object o) + * + * @param other: an other ASPath + * @return true if both have the same length and all AS's in them are in the same place. + * @author paag + */ + public boolean equals(ASPath other) { + if (this.path.size() != other.path.size()) { + return false; + } + + for (int i = 0; i < this.path.size(); i++) + if (!this.path.get(i).equals(other.path.get(i))) { + return false; + } + + return true; + } + + /** + * This is the canonical implementation of the equals() method + * + * @param o
+ */ + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (o == this) { + return true; + } + if (o instanceof ASPath) { + return this.equals((ASPath) o); + } + return false; + } + + /** + * @param as: as Autonomous System + * @return true if as generated the prefix + */ + public boolean isGenerator(AS as) { + return as.equals(this.path.getLast()); + } + + /** + * @return the As which generated the prefix + */ + public AS generator() { + try { + return this.path.getLast(); + } catch (Exception e) { + return AS.NullAS; + } + } + + /** + * Build a copy of the AS_PATH but without prepends + * + * @author paag + */ + + public ASPath canonicalPath() { + ASPath canonical = new ASPath(); + + for (int i = 0; i < this.path.size(); i++) { + AS as = this.path.get(i); + canonical.add(as); + i = this.path.lastIndexOf(as); + } + return canonical; + } + + private Vector prependers; + + /** + * @return true if the originating AS is prepending + */ + public boolean hasOriginPrepend() { + try { + return this.prependers.contains(this.generator()); + } catch (NullPointerException npe) { + return false; + } + } + + public int compareTo(ASPath aspath) { + int result = 0; + if (this.equals(aspath)) { + return 0; + } + if (this.length() != aspath.length()) { + return this.length() > aspath.length() ? 1 : -1; + } + for (int i = 0; i < this.length(); i++) { + result = this.get(i).compareTo(aspath.get(i)); + if (result == 0) { + return result; + } + } + return result; + } + + public AS getOriginating() { + return path.isEmpty() ? null : path.getLast(); + } + + public List getTransiting() { + return path.isEmpty() ? Collections.emptyList() : path.subList(0, path.size() - 1); + } +} diff --git a/src/org/javamrt/mrt/ASPathLimit.java b/src/main/java/org/javamrt/mrt/ASPathLimit.java similarity index 100% rename from src/org/javamrt/mrt/ASPathLimit.java rename to src/main/java/org/javamrt/mrt/ASPathLimit.java diff --git a/src/org/javamrt/mrt/ASPathSegment.java b/src/main/java/org/javamrt/mrt/ASPathSegment.java old mode 100644 new mode 100755 similarity index 92% rename from src/org/javamrt/mrt/ASPathSegment.java rename to src/main/java/org/javamrt/mrt/ASPathSegment.java index d9e7758..68c7623 --- a/src/org/javamrt/mrt/ASPathSegment.java +++ b/src/main/java/org/javamrt/mrt/ASPathSegment.java @@ -6,10 +6,11 @@ package org.javamrt.mrt; -import java.util.LinkedList; - +import org.javamrt.progs.route_btoa; import org.javamrt.utils.RecordAccess; +import java.util.LinkedList; + /** * The AS_PATH and AS4_PATH attributes are composed @@ -49,8 +50,8 @@ public ASPathSegment(byte[] buffer, int offset, int asSize) bLen += asSize; } } catch (Exception e) { - System.err.printf("len = %d,offset = %d; bLen = %d\n",len,offset,bLen); - RecordAccess.dump(System.err,buffer); + route_btoa.System_err_println(String.format("len = %d,offset = %d; bLen = %d", len, offset, bLen)); + RecordAccess.dump(buffer); throw e; } } diff --git a/src/org/javamrt/mrt/ASSet.java b/src/main/java/org/javamrt/mrt/ASSet.java similarity index 68% rename from src/org/javamrt/mrt/ASSet.java rename to src/main/java/org/javamrt/mrt/ASSet.java index 80d4561..459b268 100644 --- a/src/org/javamrt/mrt/ASSet.java +++ b/src/main/java/org/javamrt/mrt/ASSet.java @@ -6,16 +6,18 @@ package org.javamrt.mrt; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; // import org.javamrt.utils.RecordAccess; public class ASSet extends AS { - protected LinkedList asSet; + protected final ArrayList asSet = new ArrayList<>(); - public ASSet(LinkedList asList) { - this.asSet = new LinkedList(); + public ASSet(Collection asList) { this.asSet.addAll(asList); } @@ -33,7 +35,13 @@ private boolean equals(ASSet other) { return this.asSet.equals(other.asSet); } - public LinkedList getASList() { + @Override + public int hashCode() { + return asSet.hashCode(); + } + + @Override + public List getASList() { return this.asSet; } @@ -54,10 +62,7 @@ public AS getAS(int i) { } public String toString() { - String result = "(".concat(this.asSet.get(0).toString()); - for (int i = 1; i < asSet.size(); i++) - result = result.concat(" ").concat(this.asSet.get(i).toString()); - return result.concat(")"); + return asSet.stream().map(AS::toString).collect(Collectors.joining(" ", "(", ")")); } } diff --git a/src/org/javamrt/mrt/Advertisement.java b/src/main/java/org/javamrt/mrt/Advertisement.java similarity index 75% rename from src/org/javamrt/mrt/Advertisement.java rename to src/main/java/org/javamrt/mrt/Advertisement.java index f73e315..0bd39d9 100644 --- a/src/org/javamrt/mrt/Advertisement.java +++ b/src/main/java/org/javamrt/mrt/Advertisement.java @@ -12,21 +12,22 @@ public class Advertisement { public - Advertisement(byte[] header, - InetAddress peerIP, - AS peerAS, - Prefix prefix, - Attributes updateAttr) + Advertisement(byte[] header, + byte[] record, + InetAddress peerIP, + AS peerAS, + Prefix prefix, + Attributes updateAttr, + String updateStr) { super(header, + record, peerIP, peerAS, prefix, - updateAttr); + updateAttr, + updateStr); this.updateType = 'A'; - // - // this.updateStr = updateStr; - // } @Override diff --git a/src/org/javamrt/mrt/Advertiser.java b/src/main/java/org/javamrt/mrt/Advertiser.java similarity index 100% rename from src/org/javamrt/mrt/Advertiser.java rename to src/main/java/org/javamrt/mrt/Advertiser.java diff --git a/src/org/javamrt/mrt/Aggregator.java b/src/main/java/org/javamrt/mrt/Aggregator.java similarity index 100% rename from src/org/javamrt/mrt/Aggregator.java rename to src/main/java/org/javamrt/mrt/Aggregator.java diff --git a/src/org/javamrt/mrt/AtomAggr.java b/src/main/java/org/javamrt/mrt/AtomAggr.java similarity index 100% rename from src/org/javamrt/mrt/AtomAggr.java rename to src/main/java/org/javamrt/mrt/AtomAggr.java diff --git a/src/org/javamrt/mrt/AttrOrigin.java b/src/main/java/org/javamrt/mrt/AttrOrigin.java similarity index 100% rename from src/org/javamrt/mrt/AttrOrigin.java rename to src/main/java/org/javamrt/mrt/AttrOrigin.java diff --git a/src/org/javamrt/mrt/Attribute.java b/src/main/java/org/javamrt/mrt/Attribute.java similarity index 100% rename from src/org/javamrt/mrt/Attribute.java rename to src/main/java/org/javamrt/mrt/Attribute.java diff --git a/src/org/javamrt/mrt/AttributeException.java b/src/main/java/org/javamrt/mrt/AttributeException.java similarity index 100% rename from src/org/javamrt/mrt/AttributeException.java rename to src/main/java/org/javamrt/mrt/AttributeException.java diff --git a/src/org/javamrt/mrt/Attributes.java b/src/main/java/org/javamrt/mrt/Attributes.java similarity index 85% rename from src/org/javamrt/mrt/Attributes.java rename to src/main/java/org/javamrt/mrt/Attributes.java index 7117e46..c4a9acb 100644 --- a/src/org/javamrt/mrt/Attributes.java +++ b/src/main/java/org/javamrt/mrt/Attributes.java @@ -6,33 +6,43 @@ package org.javamrt.mrt; -import java.net.InetAddress; -import java.util.Vector; +import org.javamrt.progs.route_btoa; import org.javamrt.utils.Debug; import org.javamrt.utils.RecordAccess; +import java.net.InetAddress; +import java.util.Vector; + public class Attributes { + private final byte[] bytes; + public Attributes(byte[] record, int attrLen, int attrPos, int attrBytes) throws Exception { if (attrBytes != 2 && attrBytes != 4) throw new AttributeException(String.format( "Attributes needs attrBytes 2 or 4 (not %d", attrBytes)); - decode(record, attrLen, attrPos, attrBytes); + bytes = new byte[attrLen]; + System.arraycopy(record, attrPos, bytes, 0, attrLen); + decode(bytes, attrLen, attrBytes); } public Attributes(byte[] record, int attrLen, int attrPos) throws Exception { - decode(record, attrLen, attrPos, 2); + bytes = new byte[attrLen]; + System.arraycopy(record, attrPos, bytes, 0, attrLen); + decode(bytes, attrLen, 2); } - private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) + public byte[] getBytes() { return bytes; } + + private void decode(byte[] record, int attrLen, int attrBytes) throws Exception { byte[] buffer; - int here = attrPos; + int here = 0; if (Debug.compileDebug) - Debug.printf("Attributes(...,%d,%d,%d)\n", attrLen, attrPos, + Debug.printf("Attributes(...,%d,%d,%d)\n", attrLen, 0, attrBytes); attributes = new Vector(MRTConstants.ATTRIBUTE_TOTAL); @@ -43,7 +53,7 @@ private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) else attributes.addElement(null); - while (here < attrLen + attrPos) { + while (here < attrLen) { int flag = RecordAccess.getU8(record, here); int type = RecordAccess.getU8(record, here + 1); @@ -222,6 +232,12 @@ private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) Debug.println("ATTRIBUTE_AS4_AGGREGATOR "); break; + case MRTConstants.ATTRIBUTE_CONNECTOR: + // deprecated attribute. ignoring + if (Debug.compileDebug) + Debug.println("ATTRIBUTE_CONNECTOR (deprecated)"); + break; + // Expired but present in RRC! case MRTConstants.ATTRIBUTE_ASPATHLIMIT: @@ -233,8 +249,14 @@ private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) this.hasASPATHLimit = true; break; - default: - throw new AttributeException(type); + case MRTConstants.LARGE_COMMUNITY: + Attribute largeCommunity = new LargeCommunity(buffer); + attributes.set(MRTConstants.ATTRIBUTE_LARGE_COMMUNITY, largeCommunity); + break; + + default: + route_btoa.System_err_println("Ignoring unknown attribute type " + type); + break; } } } @@ -252,12 +274,18 @@ public String toString() { if (toStr != null) return toStr; - toStr = new String(); + toStr = ""; for (int i = MRTConstants.ATTRIBUTE_AS_PATH; i <= MRTConstants.ATTRIBUTE_AGGREGATOR; i++) { - if (attributes.elementAt(i) != null) + if (attributes.elementAt(i) != null) { + if (i == MRTConstants.ATTRIBUTE_NEXT_HOP) { + if (attributes.elementAt(MRTConstants.ATTRIBUTE_MP_REACH) != null) { + toStr = toStr.concat(attributes.elementAt(MRTConstants.ATTRIBUTE_MP_REACH).toString()).concat("|"); + continue; + } + } toStr = toStr.concat(attributes.elementAt(i).toString()); - else { + } else { if (i == MRTConstants.ATTRIBUTE_LOCAL_PREF || i == MRTConstants.ATTRIBUTE_MULTI_EXIT) toStr = toStr.concat("0"); @@ -281,6 +309,11 @@ public Community getCommunity() { return Community.empty(); } + public LargeCommunity getLargeCommunity() { + return (LargeCommunity) + attributes.elementAt(MRTConstants.ATTRIBUTE_LARGE_COMMUNITY); + } + public Med getMed() { Med result = (Med) attributes .elementAt(MRTConstants.ATTRIBUTE_MULTI_EXIT); diff --git a/src/main/java/org/javamrt/mrt/BGPFileReader.java b/src/main/java/org/javamrt/mrt/BGPFileReader.java new file mode 100755 index 0000000..d8e886c --- /dev/null +++ b/src/main/java/org/javamrt/mrt/BGPFileReader.java @@ -0,0 +1,1024 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import org.javamrt.progs.route_btoa; +import org.javamrt.utils.Debug; +import org.javamrt.utils.RecordAccess; + +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.zip.GZIPInputStream; + +public class BGPFileReader implements Closeable { + private static final boolean debug = false; + private static boolean lenient = false; + + private InputStream in; + private LinkedList recordFifo; + + private boolean eof; + public static final byte[] BGP_MARKER = new byte[]{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; + private static final byte[] UNKNOWN_IPV4 = new byte[]{0, 0, 0, 0}; + private static final byte[] UNKNOWN_IPV6 = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + private String streamName; + /***** + * + * public BGPFileReader (BufferedInputStream in) + * + * create a new BGPFileReader from BufferedInputStream + */ + + public BGPFileReader(InputStream in) { + this.in = in; + this.streamName = in.toString(); + this.recordFifo = new LinkedList(); + this.eof = false; + } + + /***** + * + * public BGPFileReader (String name) + * + * create a new BGPFileReader from BufferedInputStream specified by the + * String name + * @throws IOException + */ + + public BGPFileReader(String name) throws IOException { + InputStream inStream = null; + this.streamName = name; + + try { // to open the name as a URL + java.net.URL url = new java.net.URL(name); + inStream = url.openStream(); + } catch (java.net.MalformedURLException e) { + File inFile = new File(name); + if (!inFile.exists()) + throw new java.io.FileNotFoundException(name); + inStream = new FileInputStream(inFile); + } + + + if (this.streamName.endsWith(".gz")) { + this.in = new GZIPInputStream(inStream, 8192); + } else { + this.in = (inStream instanceof BufferedInputStream || inStream instanceof GZIPInputStream) + ? inStream : new BufferedInputStream(inStream); + } + + this.recordFifo = new LinkedList(); + this.eof = false; + } + + public BGPFileReader(File f) throws IOException { + if (!f.exists()) + throw new java.io.FileNotFoundException(f.toString()); + FileInputStream inStream = new FileInputStream(f); + this.streamName = f.getCanonicalPath(); + if (this.streamName.endsWith(".gz")) { + this.in = new GZIPInputStream(inStream, 8192); + } else { + this.in = new BufferedInputStream(inStream); + } + this.recordFifo = new LinkedList(); + this.eof = false; + } + + /*** + * void close() + * + * close the BGPFileReader + */ + public void close() throws java.io.IOException { + this.in.close(); + this.recordFifo.clear(); + this.recordFifo = null; + } + + /** + * toString(): return the name of the input Stream + */ + public String toString() { + return this.streamName; + } + + private long time = 0; + + private long recordCounter = 0; + + /** + * @return the number of MRT binary format records read.
+ * In the new MRT record formats, that has little or nothing
+ * to do with the number of BGP Events read, since a record might
+ * hold several BGP events. We need this in our quest for soft session
+ * restarts. + */ + public long mrtRecords() { + return recordCounter; + } + + /** + * The name MRT record is not perfect, because actually it's routing events we get + * @return a BGP event or a BGP control message. + * @throws Exception + */ + public MRTRecord readNext() throws Exception { + while (true) { + /* + * Consume any records waiting in the queue + * + * using recordFifo.add(MRTRecord) <=> recordFifo.remove() + */ + if (recordFifo.size() != 0) + return recordFifo.remove(); + + /* + * if the queue is empty, read from the file + */ + + final byte[] header = new byte[12]; + byte[] record; + int bytesRead; + try { + bytesRead = readFromInputStream(this.in, header, header.length); + } catch (IOException e) { + throw new MalformedBgpStreamException(e); + } + /* + * EOF + */ + if (bytesRead <= 0) { + this.eof = true; + return null; + } + /* + * truncated + */ + if (bytesRead != header.length) { + this.eof = true; + throw new MalformedBgpStreamException( + String.format("Truncated file: %d instead of %d bytes; header: %s", + bytesRead, + header.length, RecordAccess.arrayToString(header))); + } + recordCounter ++; + if (Debug.compileDebug) + RecordAccess.dump(Debug.debugStream, header); + time = RecordAccess.getU32(header, 0); + final int type = RecordAccess.getU16(header, 4); + final int subtype = RecordAccess.getU16(header, 6); + + final long recordlen = RecordAccess.getU32(header, 8); + + if (Debug.compileDebug) Debug.println("TIME: " + time + "\n TYPE: " + type + + "\n SUBTYPE: " + subtype + "\n RECORDLENGTH: " + + recordlen); + + if (recordlen > Integer.MAX_VALUE) { + throw new MalformedBgpStreamException("Can't have a record longer than 2147483647 bytes"); + } + + try { + record = new byte[(int) recordlen]; + bytesRead = readFromInputStream(this.in, record, record.length); + } catch (OutOfMemoryError e) { + throw new BGPFileReaderException( + "Got OutOfMemoryError while trying to read next record (" + recordlen + " bytes) into memory", header); + } catch (IOException e ) { + throw new MalformedBgpStreamException(e); + } + + if (bytesRead != record.length) { + final String message = String.format("Truncated file: %,d instead of %,d bytes: %s", + bytesRead, record.length, RecordAccess.arrayToString(header)); + if (lenient) { + route_btoa.System_err_println(message + "; trying to parse anyways"); + record = Arrays.copyOf(record, bytesRead); + } else { + this.eof = true; + throw new MalformedBgpStreamException(message); + } + } + + if (Debug.compileDebug) + Debug.dump(record); + /* + * Record parsing + */ + switch (type) { + case MRTConstants.TABLE_DUMP: + return parseTableDump(header, record, subtype); + + case MRTConstants.TABLE_DUMP_v2: + + switch (subtype) { + case MRTConstants.PEER_INDEX_TABLE: + parsePeerIndexTable(record); + break; + case 2: + parseTableDumpv2(header, record, MRTConstants.AFI_IPv4); + break; + case 4: + parseTableDumpv2(header, record, MRTConstants.AFI_IPv6); + break; + case 6: + parseGenericRib(); + break; + case 3: + case 5: + parseTableDumpv2Multicast(); + break; + default: + throw new BGPFileReaderException( + "Unknown TABLE_DUMP_V2 subtype" + subtype, header); + } + break; + + case MRTConstants.BGP4MP: + MRTRecord bgp4mp = parseBgp4mp(header, record, subtype); + if ((bgp4mp) != null) { + return bgp4mp; + } + break; + + case MRTConstants.BGPDUMP_TYPE_MRTD_BGP: + MRTRecord bgpRecord = parseBgp(header, record, subtype); + if ((bgpRecord) != null) { + return bgpRecord; + } + break; + default: + return new MRTRecord(header, record); + } + } + } + + /** + * Safely read from input streams that can be blocked or slow (e.g. compressed streams). + * @return the total of bytes read from the input stream or -1 if EOF is first found + */ + private static int readFromInputStream(InputStream bis, byte[] output, int length) throws IOException { + int remaining = length; + int read; + + while (((read = bis.read(output, length - remaining, remaining)) > 0) && ((remaining -= read) > 0)); + + return ((read == -1 && remaining == length) ? -1 : length - remaining); + } + + private MRTRecord parseTableDump(byte[] header, byte[] record, int subtype) throws Exception { + switch (subtype) { + case MRTConstants.AFI_IPv4: + case MRTConstants.AFI_IPv6: + return new TableDump(header, record, subtype); + default: + throw new BGPFileReaderException("Unknown TABLE_DUMP subtype" + + subtype, header); + } + + } + + private MRTRecord parseBgp(byte[] header, byte[] record, int subtype) throws Exception { + //Based on https://tools.ietf.org/html/rfc1771 + // https://tools.ietf.org/html/rfc6396 + switch (subtype) { + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_NULL: + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_PREF_UPDATE: + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_SYNC: + return new MRTRecord(header, record); + + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_STATE_CHANGE: + int offset = 0; + int asSize = 2; + int addrSize = 4; + + AS srcAs = new AS(RecordAccess.getUINT(record, offset, asSize)); + offset = asSize; + + InetAddress srcIP = InetAddress.getByAddress(RecordAccess.getBytes(record, offset, addrSize)); + offset += addrSize; + + int oldState = RecordAccess.getU16(record, offset); + offset += 2; + int newState = RecordAccess.getU16(record, offset); + + return new StateChange( + RecordAccess.getU32(header, 0), + srcIP, + srcAs, + oldState, + newState, + MRTConstants.UPDATE_STR_BGP, + header, + record); + } + + if (subtype >= MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_UPDATE && + subtype <= MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_KEEPALIVE) { + + if (record.length < 12) { + throw new BGPFileReaderException( + "Not enough bytes to read common BGP fields (at least 12 are needed): ", record); + } + final AS peerAS = new AS(Arrays.copyOfRange(record, 0, 2)); + final InetAddress peerIP = InetAddress.getByAddress(Arrays.copyOfRange(record, 2, 6)); + final AS localAS = new AS(Arrays.copyOfRange(record, 6, 8)); + final InetAddress localIP = InetAddress.getByAddress(Arrays.copyOfRange(record, 8, 12)); + + switch (subtype) { + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_UPDATE: + return getBgpUpdate(header, record, peerAS, peerIP); + + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_OPEN: + final long bgpId = RecordAccess.getUINT(record, 12 + 5, 4); + return new Open(header, record, peerIP, peerAS, bgpId); + + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_NOTIFY: + return new Notification(header, record, peerIP, peerAS); + + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_KEEPALIVE: + return new KeepAlive(header, record, peerAS, peerIP, localAS, localIP); + } + } + + return new MRTRecord(header, record); + } + + private MRTRecord getBgpUpdate(byte[] header, byte[] record, AS peerAS, InetAddress peerIP) throws Exception{ + int offset = 12; + final int asSize = 2; + + long wSize = RecordAccess.getUINT(record, offset, 2); + offset += 2; + + // WITHDRAWS + if(wSize > 0) { // Withdraw if the size > than 0. + for (int i = 0; i < wSize;) { + Prefix prefix = lenient ? new LenientPrefix(record, offset, 1) : new Nlri(record, offset, 1); + offset += prefix.getOffset(); + i += prefix.getOffset(); + + Withdraw withdraw = new Withdraw(header, record, peerIP, peerAS, prefix, MRTConstants.UPDATE_STR_BGP); + recordFifo.add(withdraw); + } + } + // Advertisments + + //Reading length of attributes + int attrLen = RecordAccess.getU16(record, offset); + offset +=2; + + Attributes attributes = null; + if(attrLen > 0){ + try { + attributes = new Attributes(record, attrLen, offset,asSize); + } catch (RFC4893Exception rfce) { + // + // piggyback peer and time info + // + rfce.setTimestamp(this.time); + rfce.setPeer(peerIP); + rfce.setAS(peerAS); + throw rfce; + } + + //Process MP_REACH and MP_UNREACH + processReachAndUnreach(header, record, attributes, peerIP, peerAS, MRTConstants.UPDATE_STR_BGP); + + offset += attrLen; + while (offset < record.length) { + Prefix aNlri = lenient ? new LenientPrefix(record, offset, 1) : new Nlri(record, offset, 1); + offset += aNlri.getOffset(); + + recordFifo.add(new Advertisement(header, record, peerIP, peerAS, + aNlri, attributes, MRTConstants.UPDATE_STR_BGP)); + } + } + if (recordFifo.isEmpty()) { + return new EndOfRib(header, record, peerIP, peerAS, attributes, MRTConstants.UPDATE_STR_BGP); + } else { + return recordFifo.remove(); + } + } + + private MRTRecord parseBgp4mp(byte[] header, byte[] record, int subtype) throws Exception { + // route_btoa.System_err_println("parseBgp4mp("+MRTConstants.mpSubType(subtype)+")"); + switch (subtype) { + case MRTConstants.BGP4MP_MESSAGE: + case MRTConstants.BGP4MP_MESSAGE_AS4: + return parseBgp4mpMessage(header, record, (subtype == MRTConstants.BGP4MP_MESSAGE) ? 2 : 4); + + /* + * TODO + * TTOODDOO:::: + * + * + * case BGP4MP_SNAPSHOT: return new Bgp4mpSnapshot(header,record); + */ + + case MRTConstants.BGP4MP_ENTRY: + return parseBgp4Entry(header, record, RecordAccess.getU16(record, 6)); + + case MRTConstants.BGP4MP_STATE_CHANGE: { + /* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer AS Number | Local AS Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Interface Index | Address Family | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer IP Address (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Local IP Address (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Old State | New State | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (record.length < 20) { + if (lenient && record.length == 8) { + // seems like known zebra/quagga 8-byte state change bug + route_btoa.System_err_println("8-byte long BGP4MP_STATE_CHANGE; parsing accordingly"); + final AS peerAs = new AS(RecordAccess.getBytes(record, 0, 2)); + final AS localAs = new AS(RecordAccess.getBytes(record, 2, 2)); + int oldState = RecordAccess.getU16(record, 4); + int newState = RecordAccess.getU16(record, 6); + return new StateChange( + RecordAccess.getU32(header, 0), + unknownAddress(MRTConstants.AFI_IPv4), + peerAs, + oldState, + newState, + MRTConstants.UPDATE_STR_BGP4MP, + header, + record); + } else { + throw new BGPFileReaderException("BGP4MP state change record smaller than 20 bytes: ", record); + } + } + int afi = RecordAccess.getU16(record, 6); + int addrOffs = 8; + int addrSize = (afi == MRTConstants.AFI_IPv4) ? 4 : 16; + int stateOffs = addrOffs + 2 * addrSize; + int oldState = RecordAccess.getU16(record, stateOffs); + stateOffs += 2; + int newState = RecordAccess.getU16(record, stateOffs); + + return new StateChange( + RecordAccess.getU32(header, 0), + InetAddress.getByAddress(RecordAccess.getBytes(record, addrOffs, addrSize)), + new AS(RecordAccess.getU16(record, 0)), + oldState, + newState, + MRTConstants.UPDATE_STR_BGP4MP, + header, + record); + + } + + case MRTConstants.BGP4MP_STATE_CHANGE_AS4: { + /* + * draft-ietf-grow-mrt-07.txt + * + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer AS number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Local AS number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Interface Index | Address Family | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer IP address (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Local IP address (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Old State | New State | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + int afi = RecordAccess.getU16(record, 10); + int addrSize = (afi == MRTConstants.AFI_IPv4) ? 4 : 16; + int addrOffs = 12; + int stateOffs = addrOffs + 2 * addrSize; + int oldState = RecordAccess.getU16(record, stateOffs); + stateOffs += 2; + int newState = RecordAccess.getU16(record, stateOffs); + + return new StateChange( + RecordAccess.getU32(header, 0), + InetAddress.getByAddress(RecordAccess.getBytes(record, addrOffs, addrSize)), + new AS(RecordAccess.getU32(record, 0)), + oldState, + newState, + MRTConstants.UPDATE_STR_BGP4MP, + header, + record); + + } + + default: + break; + } + + return new MRTRecord(header, record); + } + + private void parsePeerIndexTable(byte[] record) throws Exception { + /* + * route_btoa.System_err_println("in BGPFileReader.parsePeerIndexTable\nheader:"); + * RecordAccess.dump(header); route_btoa.System_err_println("record"); + * RecordAccess.dump(record); + */ + // route_btoa.System_err_println("\nin parsePeerIndexTable()..."); + /* + * 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * Collector BGP ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * View Name Length | View Name (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + * Peer Count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + int here = 0; + // long CollectorBGPId = RecordAccess.getU32 (this.record,here); + here += 4; + int ViewNameLen = RecordAccess.getU16(record, here); + here += 2; + // String ViewName = null; + if (ViewNameLen > 0) { + // TODO extract ViewName + here += ViewNameLen; + } + int PeerCount = RecordAccess.getU16(record, here); + here += 2; + + /* + * route_btoa.System_err_println(String.format("Collector BGP Id: 0x%08X",CollectorBGPId)); + * route_btoa.System_err_println(String.format("View Name Length = %d",ViewNameLen)); + * route_btoa.System_err_println(String.format(" has %d peers\n",PeerCount)); + */ + bgpId = new long[PeerCount]; + peerAS = new org.javamrt.mrt.AS[PeerCount]; + peerIP = new InetAddress[PeerCount]; + + for (int i = 0; i < PeerCount; i++) { + /* + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer BGP ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer IP address (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Peer AS (variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The Peer Type field is a bit field which encodes the type of the + * AS and IP address as follows: + * + * Bit 0 - unset for IPv4 Peer IP address, set for IPv6 Bit 1 - + * unset when Peer AS field is 16 bits, set when it's 32 bits + */ + int peerType = RecordAccess.getU8(record, here++); + bgpId[i] = RecordAccess.getU32(record, here); + here += 4; + if ((peerType & 0x01) == 0) { + peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( + record, here, 4)); + here += 4; + } else { + peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( + record, here, 16)); + here += 16; + } + if ((peerType & 0x02) == 0) { + peerAS[i] = new AS(RecordAccess.getU16(record, here)); + here += 2; + } else { + peerAS[i] = new AS(RecordAccess.getU32(record, here)); + here += 4; + } + + // route_btoa.System_err_println("Peer "+i+"("+bgpId[i]+"): "+peerIP[i].getHostAddress()+" "+peerAS[i]); + } + + // route_btoa.exit(0); + } + + private long[] bgpId = null; + private org.javamrt.mrt.AS peerAS[] = null; + private java.net.InetAddress peerIP[] = null; + + private void parseTableDumpv2(byte[] header, byte[] record, int nlriType) throws Exception { + + if (Debug.compileDebug) { + Debug.printf("parseTableDumpv2(%d)\nheader:", nlriType); + Debug.dump(header); + Debug.println("record:"); + Debug.dump(record); + } + + int offset = 0; + long sequenceNo = RecordAccess.getU32(record, offset); + offset = 4; + Prefix nlri = lenient ? new LenientPrefix(record, offset, nlriType) : new Nlri(record, offset, nlriType); + offset += nlri.getOffset(); + + int entryCount = RecordAccess.getU16(record, offset); + offset += 2; + + if (debug) { + route_btoa.System_err_println("Sequence = " + sequenceNo); + route_btoa.System_err_println("NLRI = " + nlri.toString() + + " [" + nlri.getOffset() + "]"); + route_btoa.System_err_println("entries = " + entryCount); + } + + for (int i = 0; i < entryCount; i++) { + int peerIndex = RecordAccess.getU16(record, offset); + + if (debug) { + route_btoa.System_err_println(String.format("peerIndex = %d; peer = %s(%s)\n", + peerIndex, MRTConstants.ipAddressString(peerIP[peerIndex], false), peerAS[peerIndex].toString("AS"))); + } + + offset += 2; + // TODO use origTime if appropriate + // + // long timeOrig=RecordAccess.getU32(this.record,offset); + offset += 4; + int attrLen = RecordAccess.getU16(record, offset); + offset += 2; + Attributes attributes = new Attributes(record, attrLen, offset,4); + offset += attrLen; + + + recordFifo.add(new TableDumpv2( + 1, // int view, + (int) (sequenceNo & 0xffff), + nlri, + time, + peerIP[peerIndex], // InetAddr + peerAS[peerIndex], + attributes, + header, + record)); + } + } + + //Process MP_REACH and MP_UNREACH + private void processReachAndUnreach(byte[] header, byte[] record, Attributes attributes, InetAddress srcIP, AS srcAs, String updateStrType) throws Exception{ + MpUnReach mpUnreach = (MpUnReach) attributes.getAttribute(MRTConstants.ATTRIBUTE_MP_UNREACH); + + if (mpUnreach != null) { + for (Nlri mpu : mpUnreach.getNlri()) { + recordFifo.add(new Withdraw(header, record, srcIP, srcAs, mpu, updateStrType)); + } + } + + MpReach mpReach = (MpReach) attributes.getAttribute(MRTConstants.ATTRIBUTE_MP_REACH); + + if (mpReach != null) { + if (Debug.compileDebug) Debug.printf("Has MP_REACH (%s)\n",mpReach.getNlri()); + for (Nlri mpu : mpReach.getNlri()) { + recordFifo.add(new Advertisement(header, record, srcIP, srcAs, mpu, + attributes, updateStrType)); + } + } + + } + + private MRTRecord parseBgp4mpMessage(byte[] header, byte[] record, int asSize) throws Exception { + /* + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Peer AS Number | Local AS Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Interface Index | Address Family | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Peer IP Address (variable) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Local IP Address (variable) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BGP Message... (variable) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Figure 12: BGP4MP_MESSAGE Subtype + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Peer AS Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Local AS Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Interface Index | Address Family | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Peer IP Address (variable) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Local IP Address (variable) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BGP Message... (variable) + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + Figure 13: BGP4MP_MESSAGE_AS4 Subtype + */ + + final int markerPosition = lenient ? + findBgpMarker(record, asSize) : + record.length; + + final ByteBuffer buffer = ByteBuffer.wrap(record, 0, markerPosition); + + AS srcAs = notLenientOr(buffer.remaining() >= asSize) ? new AS(RecordAccess.getUINT(buffer, asSize)) : new AS(0L); + AS dstAs = notLenientOr(buffer.remaining() >= asSize) ? new AS(RecordAccess.getUINT(buffer, asSize)) : new AS(0L); + int iface = notLenientOr(buffer.remaining() >= 2) ? RecordAccess.getU16(buffer) : 0; + int afi = notLenientOr(buffer.remaining() >= 2) ? RecordAccess.getU16(buffer) : 0; + if (afi != MRTConstants.AFI_IPv4 && afi != MRTConstants.AFI_IPv6) { + final String message = String.format("AFI=%d is unknown", afi); + if (lenient) { + route_btoa.System_err_println(message + "; assuming IPv4"); + } else { + throw new BGPFileReaderException(message, record); + } + } + int addrSize = (afi == MRTConstants.AFI_IPv6) ? 16 : 4; + + InetAddress srcIP = notLenientOr(buffer.remaining() >= addrSize) ? + InetAddress.getByAddress(RecordAccess.getBytes(buffer, addrSize)) : + unknownAddress(afi); + InetAddress dstIP = notLenientOr(buffer.remaining() >= addrSize) ? + InetAddress.getByAddress(RecordAccess.getBytes(buffer, addrSize)) : + unknownAddress(afi); + /* + * skip the following 16 bytes which are the signature of the BGP header + */ + int offset = buffer.position() + BGP_MARKER.length; + + int bgpSize = RecordAccess.getU16(record, offset); offset += 2; + int bgpType = RecordAccess.getU8(record, offset); offset ++; + + if (Debug.compileDebug) { + Debug.printf("Bgp4Update(asSize = %d)\n", asSize); + Debug.printf("AS srcAs = %s\n", srcAs.toString()); + Debug.printf("AS dstAs = %s\n", dstAs.toString()); + Debug.printf("int iface = %d\n", iface); + Debug.printf("int Afi = %d\n", afi); + Debug.printf("int addrSize = %d\n", addrSize); + Debug.println("srcIP = " + srcIP.getHostAddress()); + Debug.println("dstIP = " + dstIP.getHostAddress()); + Debug.printf("bgpSize = %d\n", bgpSize); + Debug.println("bgpType = " + MRTConstants.bgpType(bgpType)); + Debug.dump(record); + } + switch (bgpType) { + case MRTConstants.BGP4MSG_KEEPALIVE: + return new KeepAlive(header, record, srcAs, srcIP, dstAs, dstIP); + + case MRTConstants.BGP4MSG_OPEN: + int offsetForOpen = offset+5; + long bgpId = RecordAccess.getU32(record, offsetForOpen); + return new Open(header, record, srcIP, srcAs, bgpId); + + case MRTConstants.BGP4MSG_NOTIFICATION: + return new Notification(header, record, srcIP, srcAs); + + case MRTConstants.BGP4MSG_UPDATE: + break; // to continue after case() + + case MRTConstants.BGP4MSG_REFRESH: + return new Refresh(header, record); + + default: + throw new Exception("Unknown BGP4 record type(" + bgpType + ")"); + } + + /* + * Here is where the update starts + */ + + int unfeasibleLen = RecordAccess.getU16(record, offset); + offset += 2; + if (Debug.compileDebug) Debug.printf("int unfeasibleLen = %d\n",unfeasibleLen); + + + for (int i = 0; i < unfeasibleLen;) { + //The withdraws out of the Attributs are going to be always ipv4 + Prefix wNlri = lenient ? new LenientPrefix(record, offset, 1) : new Nlri(record, offset, 1); + offset += wNlri.getOffset(); + i += wNlri.getOffset(); + + recordFifo + .add(new Withdraw(header, record, srcIP, srcAs, wNlri,MRTConstants.UPDATE_STR_BGP4MP)); + } + + int attrLen = RecordAccess.getU16(record, offset); + if (Debug.compileDebug) Debug.printf("attrLen = %d, offset =%d (%d)\n",attrLen,offset,offset+attrLen+2); + offset += 2; + + if (offset + attrLen > record.length) { + if (lenient) { + route_btoa.System_err_println("Attribute length goes over the record length, truncating"); + attrLen = record.length - offset; + } + } + + Attributes attributes = null; + if (attrLen > 0) { + try { + attributes = new Attributes(record, attrLen, offset,asSize); + } catch (RFC4893Exception rfce) { + // + // piggyback peer and time info + // + rfce.setTimestamp(this.time); + rfce.setPeer(srcIP); + rfce.setAS(srcAs); + throw rfce; + } + + // Process MP_REACH and MP_UNREACH + processReachAndUnreach(header, record, attributes, srcIP, srcAs, MRTConstants.UPDATE_STR_BGP4MP); + + +// MpUnReach mpUnreach = (MpUnReach) attributes +// .getAttribute(MRTConstants.ATTRIBUTE_MP_UNREACH); +// +// if (mpUnreach != null) { +// for (Nlri mpu : mpUnreach.getNlri()) { +// recordFifo.add(new Withdraw(header, record, srcIP, srcAs, mpu.toPrefix(), MRTConstants.UPDATE_STR_BGP4MP)); +// } +// } +// +// MpReach mpReach = (MpReach) attributes +// .getAttribute(MRTConstants.ATTRIBUTE_MP_REACH); +// +// if (mpReach != null) { +// if (Debug.compileDebug) Debug.printf("Has MP_REACH (%s)\n",mpReach.getNlri()); +// for (Nlri mpu : mpReach.getNlri()) { +// recordFifo.add(new Advertisement(header, record, srcIP, srcAs, mpu +// .toPrefix(), attributes, MRTConstants.UPDATE_STR_BGP4MP)); +// } +// } + + /* + * if (mpReach != null || mpUnreach != null) + * route_btoa.System_err_println("This is the whole record"); + * RecordAccess.dump(record); + * route_btoa.System_err_println("These are the attributes"); + * RecordAccess.dump(record,offset,attrLen); + * route_btoa.System_err_println("int attrLen = "+attrLen); + * + * throw new Exception("MP_REACH attribute!"); + */ + + offset += attrLen; + + if (Debug.compileDebug) Debug.debug("offset(%d) record.length (%d)\n",offset,record.length); + while (offset < record.length) { + //The announcements out of the Attributs are going to be always ipv4 + Prefix aNlri = lenient ? new LenientPrefix(record, offset, 1) : new Nlri(record, offset, 1); + offset += aNlri.getOffset(); + + recordFifo.add(new Advertisement(header, record, srcIP, srcAs, + aNlri, attributes, MRTConstants.UPDATE_STR_BGP4MP)); + } + } + if (recordFifo.isEmpty()) { + return new EndOfRib(header, record, srcIP, srcAs, attributes, MRTConstants.UPDATE_STR_BGP4MP); + } else { + return recordFifo.remove(); + } + } + + private InetAddress unknownAddress(int afi) { + try { + if (afi == MRTConstants.AFI_IPv6) return InetAddress.getByAddress(UNKNOWN_IPV6); + else return InetAddress.getByAddress(UNKNOWN_IPV4); + } catch (UnknownHostException e) { + // should not happen + throw new RuntimeException(e); + } + } + + private int findBgpMarker(byte[] record, int asSize) throws BGPFileReaderException { + final int endOfSearch = record.length - BGP_MARKER.length; + if (endOfSearch < 0) { + throw new BGPFileReaderException("BGP4MP message is too small: ", record); + } + final int expectedPositionIpv4 = 2 * asSize + 2 + 2 + 2 * 4; + final int expectedPositionIpv6 = 2 * asSize + 2 + 2 + 2 * 16; + + if (expectedPositionIpv4 <= endOfSearch && isBgpMarkerAt(record, expectedPositionIpv4)) { + return expectedPositionIpv4; + } else if (expectedPositionIpv6 <= endOfSearch && isBgpMarkerAt(record, expectedPositionIpv6)) { + return expectedPositionIpv6; + } else { + int i = 0; + while (i < endOfSearch) { + if (isBgpMarkerAt(record, i)) { + route_btoa.System_err_println(String.format("BGP marker found at offset %d; record re-aligned%n", i)); + return i; + } else { + i++; + } + } + } + throw new BGPFileReaderException("BGP4MP message without BGP marker: ", record); + } + + private boolean isBgpMarkerAt(byte[] bytes, int offset) { + return bytes[offset] == BGP_MARKER[0] + && Arrays.equals(Arrays.copyOfRange(bytes, offset, offset + BGP_MARKER.length), BGP_MARKER); + } + + private MRTRecord parseBgp4Entry(byte[] header, byte[] record, int AFI) throws Exception { + /* + * TODO: this doesn't work as expected yet + */ + if (Debug.compileDebug) { + Debug.debug("in parseBgp4Entry\n"); + Debug.dump(record); + } + int addrSize = (AFI == MRTConstants.AFI_IPv4) ? 4 : 16; + + int view = RecordAccess.getU16(record, 0); + int status = RecordAccess.getU16(record, 2); + long rtime = RecordAccess.getU32(record, 4); + int af = RecordAccess.getU16(record, 8); + int safi = RecordAccess.getU8(record, 10); + int nhl = RecordAccess.getU8(record, 11); + + if (Debug.compileDebug) { + Debug.debug("int view = %d\n",view); + Debug.debug("int status = %d\n",status); + Debug.debug("long rtime = %d\n",rtime); + Debug.debug("int af = %d\n",af); + Debug.debug("int safi = %d\n",safi); + Debug.debug("int nhl = %d\n",nhl); + } + int offset = 12; + InetAddress nextHop = InetAddress.getByAddress(RecordAccess.getBytes( + record, offset, addrSize)); + offset += addrSize; + Prefix prefix = lenient ? new LenientPrefix(record, offset, AFI) : new Nlri(record, offset, AFI); + offset += prefix.getOffset(); + + Attributes attrs = new Attributes(record, record.length - offset, + offset); + ASPath aspath = attrs.getASPath(); + + AS neighborAS = null; + + if (aspath != null) + neighborAS = aspath.get(0); + + return new TableDumpv2(view, 1, prefix, rtime, nextHop, neighborAS, + attrs, header, record); + } + + private void parseGenericRib() throws BGPFileReaderException{ + // TODO: implement + throw new BGPFileReaderException("TODO : parseGenericRib",new byte[1]); + } + + private void parseTableDumpv2Multicast() throws BGPFileReaderException { + // TODO: implement + throw new BGPFileReaderException("TODO: parseTableDumpv2Multicast()",new byte[1]); + } + + public boolean eof() { + return this.eof; + } + + public static void setLenient(boolean isLenient) { + lenient = isLenient; + } + + public static boolean isLenient() { + return lenient; + } + + public static boolean notLenientOr(boolean condition) { + return !lenient || condition; + } + +} diff --git a/src/org/javamrt/mrt/BGPFileReaderException.java b/src/main/java/org/javamrt/mrt/BGPFileReaderException.java similarity index 100% rename from src/org/javamrt/mrt/BGPFileReaderException.java rename to src/main/java/org/javamrt/mrt/BGPFileReaderException.java diff --git a/src/org/javamrt/mrt/Bgp4Update.java b/src/main/java/org/javamrt/mrt/Bgp4Update.java similarity index 75% rename from src/org/javamrt/mrt/Bgp4Update.java rename to src/main/java/org/javamrt/mrt/Bgp4Update.java index de75eb2..f0105f7 100644 --- a/src/org/javamrt/mrt/Bgp4Update.java +++ b/src/main/java/org/javamrt/mrt/Bgp4Update.java @@ -8,48 +8,54 @@ import java.net.InetAddress; import java.util.Comparator; +import java.util.Objects; public class Bgp4Update extends MRTRecord implements Comparable, Comparator { protected char updateType = '?'; - protected String updateStr = "BGP4MP"; + protected String updateStr = ""; protected InetAddress peerIP = null; protected AS peerAS = new AS(0); protected Prefix prefix = null; protected Attributes updateAttr = null; - public Bgp4Update(byte[] header, InetAddress peerIP, AS peerAS, - Prefix prefix, Attributes updateAttr) { - super(header); + public Bgp4Update(byte[] header, byte[] record, InetAddress peerIP, AS peerAS, + Prefix prefix, Attributes updateAttr, String updateStr) { + super(header, record); this.peerIP = peerIP; this.peerAS = peerAS; this.prefix = prefix; this.updateAttr = updateAttr; + this.updateStr = updateStr; } - public Bgp4Update(byte[] header, InetAddress peerIP, AS peerAS, - Prefix prefix) { - super(header); + public Bgp4Update(byte[] header, byte[] record, InetAddress peerIP, AS peerAS, + Prefix prefix, String updateStr) { + super(header, record); this.peerIP = peerIP; this.peerAS = peerAS; this.prefix = prefix; this.updateAttr = null; + this.updateStr = updateStr; } public String toString() { - String peerString = MRTConstants.ipAddressString(this.peerIP); + String peerString = MRTConstants.ipAddressString(this.peerIP, false); - String result = this.updateStr + "|" + this.time + "|" - + // this comes from MRTRecord - this.updateType + "|" + peerString + "|" - + this.peerAS + "|" + this.prefix.toString(); + StringBuilder result = new StringBuilder(this.updateStr).append('|') + .append(getTime()).append('|') + // this comes from MRTRecord + .append(this.updateType).append('|') + .append(peerString).append('|') + .append(this.peerAS).append('|') + .append(this.prefix == null ? "" : this.prefix.toString()); if (this.updateAttr != null) - result += '|' + this.updateAttr.toString(); + result.append('|').append(this.updateAttr.toString()); - return result; + return result.toString(); } /* @@ -65,11 +71,11 @@ public String toString() { public boolean isIPv4() { - return this.prefix.isIPv4(); + return prefix != null && prefix.isIPv4(); } public boolean isIPv6() { - return this.prefix.isIPv6(); + return prefix != null && prefix.isIPv6(); } public Prefix getPrefix() { @@ -103,19 +109,16 @@ public Attributes getAttributes() { * Order by prefixes, then by peer and then by time. */ public int compareTo(Bgp4Update other) { - int result = this.prefix.compareTo(other.prefix); + int result = Objects.compare(this.prefix, other.prefix, Prefix::compareTo); if (result == 0) { result = org.javamrt.utils.InetAddressComparator.compara(this.peerIP, other.peerIP); if (result == 0) { - if (this.time < other.time) - result = -1; - else if (this.time > other.time) - result = 1; + result = Long.compare(this.getTime(), other.getTime()); /* * Ignore sorting by update type * - * else { + * if (result == 0) { if (this.updateType > other.updateType) result = 1; else if (this.updateType < other.updateType) @@ -145,7 +148,7 @@ public int compare(Bgp4Update o1, Bgp4Update o2) { */ public boolean equals(Bgp4Update o) { if (this.updateType == ((Bgp4Update)o).updateType - && this.time == ((Bgp4Update)o).time + && this.getTime() == ((Bgp4Update)o).getTime() && this.peerIP.equals(((Bgp4Update)o).peerIP) && this.prefix.equals(((Bgp4Update)o).prefix)) { // Advertisements => compare Attributes diff --git a/src/org/javamrt/mrt/ClusterId.java b/src/main/java/org/javamrt/mrt/ClusterId.java similarity index 100% rename from src/org/javamrt/mrt/ClusterId.java rename to src/main/java/org/javamrt/mrt/ClusterId.java diff --git a/src/org/javamrt/mrt/ClusterList.java b/src/main/java/org/javamrt/mrt/ClusterList.java similarity index 81% rename from src/org/javamrt/mrt/ClusterList.java rename to src/main/java/org/javamrt/mrt/ClusterList.java index adedb24..ab1bc5f 100644 --- a/src/org/javamrt/mrt/ClusterList.java +++ b/src/main/java/org/javamrt/mrt/ClusterList.java @@ -49,12 +49,14 @@ public boolean equals(Object o) { return false; } - public String toString() { - StringBuffer result = new StringBuffer(); + public String toString() { + StringBuilder result = new StringBuilder(); - for (int i = 0; i < clusterList.length; i++) - result.append(clusterList[i].getHostAddress() + " "); + for (final InetAddress aClusterList : clusterList) { + result.append(aClusterList.getHostAddress()) + .append(" "); + } - return result.toString(); - } + return result.toString(); + } } diff --git a/src/org/javamrt/mrt/Community.java b/src/main/java/org/javamrt/mrt/Community.java similarity index 81% rename from src/org/javamrt/mrt/Community.java rename to src/main/java/org/javamrt/mrt/Community.java index f6f2c7c..d1744b6 100644 --- a/src/org/javamrt/mrt/Community.java +++ b/src/main/java/org/javamrt/mrt/Community.java @@ -8,6 +8,8 @@ import org.javamrt.utils.RecordAccess; +import java.util.Arrays; + public class Community implements Attribute { static final private int BGP_ATTR_COMMUNITY_NO_EXPORT = 0xFFFFFF01; @@ -23,8 +25,7 @@ private Community() { public Community(byte[] buffer) { community = new byte[buffer.length]; - for (int i = 0; i < buffer.length; i++) - community[i] = buffer[i]; + System.arraycopy(buffer, 0, community, 0, buffer.length); } public static Community empty() { @@ -32,11 +33,11 @@ public static Community empty() { } public String toString() { - return toStringBuffer(community).toString(); + return toStringBuilder(community).toString(); } - private static StringBuffer toStringBuffer(byte[] buffer) { - StringBuffer result = new StringBuffer(); + private static StringBuilder toStringBuilder(byte[] buffer) { + StringBuilder result = new StringBuilder(); if (null != buffer) { int len = buffer.length; @@ -66,12 +67,7 @@ else if (u32Community == BGP_ATTR_COMMUNITY_NOPEER) } public boolean equals(Community other) { - if (this.community.length != other.community.length) - return false; - for (int i = 0; i < this.community.length; i++) - if (this.community[i] != other.community[i]) - return false; - return true; + return Arrays.equals(this.community, other.community); } public boolean equals(Object o) { diff --git a/src/org/javamrt/mrt/Dpa.java b/src/main/java/org/javamrt/mrt/Dpa.java similarity index 100% rename from src/org/javamrt/mrt/Dpa.java rename to src/main/java/org/javamrt/mrt/Dpa.java diff --git a/src/main/java/org/javamrt/mrt/EndOfRib.java b/src/main/java/org/javamrt/mrt/EndOfRib.java new file mode 100644 index 0000000..b7e3a2f --- /dev/null +++ b/src/main/java/org/javamrt/mrt/EndOfRib.java @@ -0,0 +1,24 @@ +package org.javamrt.mrt; + +import java.net.InetAddress; + +public class EndOfRib extends Bgp4Update { + protected char updateType = 0; + + public EndOfRib(byte[] header, byte[] record, InetAddress peerIP, AS peerAS, String updateStr) { + super(header, record, peerIP, peerAS, null, updateStr); + } + + public EndOfRib(byte[] header, byte[] record, InetAddress peerIP, AS peerAS, Attributes updateAttr, String updateStr) { + super(header, record, peerIP, peerAS, null, updateAttr, updateStr); + } + + @Override + public String toString() { + String peerString = MRTConstants.ipAddressString(this.peerIP, false); + String attrString = updateAttr == null ? "" : updateAttr.toString(); + + return String.format("%s|%s||%s|%s|%s", + updateStr, getTime(), peerString, peerAS, attrString); + } +} diff --git a/src/org/javamrt/mrt/ExtCommunities.java b/src/main/java/org/javamrt/mrt/ExtCommunities.java similarity index 98% rename from src/org/javamrt/mrt/ExtCommunities.java rename to src/main/java/org/javamrt/mrt/ExtCommunities.java index 6206d04..46933f6 100644 --- a/src/org/javamrt/mrt/ExtCommunities.java +++ b/src/main/java/org/javamrt/mrt/ExtCommunities.java @@ -37,7 +37,6 @@ public ExtCommunities(byte[] buffer) throws Exception { } else if (typeHigh == 0x01) { byte[] value = RecordAccess.getBytes(buffer, offset, 4); - offset += 4; ip = InetAddress.getByAddress(value).getHostAddress(); offset += 4; number = (long) RecordAccess.getU16(buffer, offset); diff --git a/src/org/javamrt/mrt/IndexTable.java b/src/main/java/org/javamrt/mrt/IndexTable.java old mode 100644 new mode 100755 similarity index 70% rename from src/org/javamrt/mrt/IndexTable.java rename to src/main/java/org/javamrt/mrt/IndexTable.java index 2eccb6e..9ce94fc --- a/src/org/javamrt/mrt/IndexTable.java +++ b/src/main/java/org/javamrt/mrt/IndexTable.java @@ -5,12 +5,14 @@ // http://www.gnu.org/licenses/lgpl-3.0-standalone.html package org.javamrt.mrt; + +import org.javamrt.progs.route_btoa; +import org.javamrt.utils.RecordAccess; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Vector; -import org.javamrt.utils.RecordAccess; - public class IndexTable @@ -26,14 +28,14 @@ public class IndexTable byte[] collectorIdbyte = RecordAccess.getBytes (this.record, offset, 4); /* try { - System.out.println(InetAddress.getByAddress(collectorIdbyte).getHostAddress()); + route_btoa.System_err_println(InetAddress.getByAddress(collectorIdbyte).getHostAddress()); } catch (UnknownHostException e) { - e.printStackTrace(); + route_btoa.printStackTrace(e); } */ offset += 4; int nameLength = RecordAccess.getU16 (this.record, offset); - //System.out.println(nameLength); + //route_btoa.System_err_println(nameLength); offset += 2; // byte[] viewName = null; if (nameLength > 0) @@ -44,7 +46,7 @@ public class IndexTable peerCount = RecordAccess.getU16 (this.record, offset); offset += 2; /* - System.out.println("\nRecord: "+this.record.length+"\nCollectorId: "+collectorId + route_btoa.System_err_println("\nRecord: "+this.record.length+"\nCollectorId: "+collectorId +"\nnameLength: "+nameLength+"\nfirstMask(byte): "+ "\npeerCount: "+peerCount); */ @@ -63,10 +65,10 @@ public class IndexTable int firstBit; int secondBit; //byte[] peerCount2=RecordAccess.getBytes(this.record, offset, 1); - //System.out.println("Bit 1: "+peerType); + //route_btoa.System_err_println("Bit 1: "+peerType); if ((peerType & bit0) != 0) { - //System.out.println("upit (peertype&bit0) tacan!"); + //route_btoa.System_err_println("upit (peertype&bit0) tacan!"); firstBit = 16; } else @@ -74,18 +76,18 @@ public class IndexTable if ((peerType & bit1) != 0) { secondBit = 4; - //System.out.println("upit (peertype&bit1) tacan!"); + //route_btoa.System_err_println("upit (peertype&bit1) tacan!"); } else secondBit = 2; - //System.out.println("FirstBit: "+firstBit+"\nSecond Bit: "+secondBit+"\nOFFSET: "+offset); + //route_btoa.System_err_println("FirstBit: "+firstBit+"\nSecond Bit: "+secondBit+"\nOFFSET: "+offset); long peerBgpId = RecordAccess.getU32 (this.record, offset); - //System.out.println("peerBgpId: "+peerBgpId+" numero i: "+i); + //route_btoa.System_err_println("peerBgpId: "+peerBgpId+" numero i: "+i); peerBgp[i] = peerBgpId; - //System.out.println("peerBgpId: "+peerBgpId); + //route_btoa.System_err_println("peerBgpId: "+peerBgpId); offset += 4; - //System.out.println("firstBIt: "+firstBit+" offset: "+offset+" recordlen: "+this.record.length); + //route_btoa.System_err_println("firstBIt: "+firstBit+" offset: "+offset+" recordlen: "+this.record.length); try { @@ -95,12 +97,12 @@ public class IndexTable firstBit)); } catch (UnknownHostException e1) { - e1.printStackTrace (); + route_btoa.printStackTrace(e1); } peerIp.set (i, takeIp); /* try{ - System.out.println("atresa1 je: "+InetAddress.getByAddress(RecordAccess.getBytes(this.record, offset, firstBit)).getHostAddress()); + route_btoa.System_err_println("atresa1 je: "+InetAddress.getByAddress(RecordAccess.getBytes(this.record, offset, firstBit)).getHostAddress()); } catch(UnknownHostException e){} */ @@ -111,10 +113,10 @@ public class IndexTable else takePeerAs = RecordAccess.getU32 (this.record, offset); peerAs[i] = takePeerAs; - //System.out.println("PeerAs je: "+RecordAccess.getBytes(this.record,offset, secondBit)); + //route_btoa.System_err_println("PeerAs je: "+RecordAccess.getBytes(this.record,offset, secondBit)); offset += secondBit; } - //for (int i=0;i record.length) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Not enough input bytes (%s) to read NLRI prefix (%d bytes from offset %d)", + RecordAccess.arrayToString(record), nrBytes(), offset)); + } + System.arraycopy(record, offset, base, 0, nrBytes()); + isInIpv4EmbeddedIpv6Format = MRTConstants.isInIpv4EmbeddedIpv6Format(this.base); + } + + @Override + public InetAddress getBroadcastAddress() { + throw new UnsupportedOperationException("Not implemented in a lenient prefix"); + } + + @Override + protected boolean matches(byte[] addr) { + throw new UnsupportedOperationException("Not implemented in a lenient prefix"); + } +} diff --git a/src/org/javamrt/mrt/LocalPref.java b/src/main/java/org/javamrt/mrt/LocalPref.java similarity index 95% rename from src/org/javamrt/mrt/LocalPref.java rename to src/main/java/org/javamrt/mrt/LocalPref.java index bdaa7c1..bf2bcbb 100644 --- a/src/org/javamrt/mrt/LocalPref.java +++ b/src/main/java/org/javamrt/mrt/LocalPref.java @@ -15,7 +15,7 @@ public class LocalPref implements Attribute { } public String toString() { - return "" + localPref; + return Long.toString(localPref); } public long getLocalPref() { diff --git a/src/org/javamrt/mrt/MRTConstants.java b/src/main/java/org/javamrt/mrt/MRTConstants.java similarity index 59% rename from src/org/javamrt/mrt/MRTConstants.java rename to src/main/java/org/javamrt/mrt/MRTConstants.java index 220204b..b557bf1 100644 --- a/src/org/javamrt/mrt/MRTConstants.java +++ b/src/main/java/org/javamrt/mrt/MRTConstants.java @@ -6,8 +6,13 @@ package org.javamrt.mrt; + +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.text.SimpleDateFormat; +import java.util.Arrays; public class MRTConstants { @@ -17,8 +22,8 @@ public class MRTConstants { public static final int asSet = 1; public static final int asSequence = 2; - public static final int asConfedSet = 3; - public static final int asConfedSequence = 4; + public static final int asConfedSequence = 3; + public static final int asConfedSet = 4; public static String asPathString(int type) { switch (type) { @@ -55,6 +60,8 @@ public static String asPathString(int type) { public static final byte EXT_COMMUNITIES = 16; public static final byte AS4_PATH = 17; public static final byte AS4_AGGREGATOR = 18; + public static final byte LARGE_COMMUNITY = 32; + public static final int ATTR_SET = 128; public static final int ATTRIBUTE_AS_PATH = 0; public static final int ATTRIBUTE_ORIGIN = 1; @@ -74,13 +81,18 @@ public static String asPathString(int type) { public static final int ATTRIBUTE_EXT_COMMUNITIES = 15; public static final int ATTRIBUTE_AS4_PATH = 16; public static final int ATTRIBUTE_AS4_AGGREGATOR = 17; + public static final int ATTRIBUTE_CONNECTOR = 20; public static final int ATTRIBUTE_ASPATHLIMIT = 21; - public static final int ATTRIBUTE_TOTAL = 22; + public static final int ATTRIBUTE_LARGE_COMMUNITY = 22; + public static final int ATTRIBUTE_TOTAL = 256; public static final int AFI_IPv4 = 1; public static final int AFI_IPv6 = 2; public static final int AFI_MAX = AFI_IPv6; + public static final int IPv4_LENGTH = 4; + public static final int IPv6_LENGTH = 16; + public static final int SAFI_UNICAST = 1; public static final int SAFI_MULTICAST = 2; public static final int SAFI_UNICAST_MULTICAST = 3; @@ -110,7 +122,20 @@ public static String asPathString(int type) { public static final int BGP4MSG_KEEPALIVE = 4; public static final int BGP4MSG_REFRESH = 5; - public static final String mpSubType(int s) { + public static final int BGPDUMP_TYPE_MRTD_BGP = 5; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_NULL= 0; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_UPDATE= 1; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_PREF_UPDATE= 2; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_STATE_CHANGE = 3; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_SYNC = 4; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_OPEN = 5; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_NOTIFY = 6; + public static final int BGPDUMP_SUBTYPE_MRTD_BGP_KEEPALIVE = 7; + + public static final String UPDATE_STR_BGP4MP = "BGP4MP"; + public static final String UPDATE_STR_BGP = "BGP"; + + public static String mpSubType(int s) { switch (s) { case BGP4MP_STATE_CHANGE: return "BGP4MP_STATE_CHANGE"; @@ -129,7 +154,7 @@ public static final String mpSubType(int s) { } } - public static final String attrFlags(byte attr) { + public static String attrFlags(byte attr) { String result = ""; if (0 != (attr & BGP_ATTR_FLAG_OPTIONAL)) result = result + "OPTIONAL "; if (0 != (attr & BGP_ATTR_FLAG_TRANS)) result = result + "TRANSITIVE "; @@ -148,7 +173,7 @@ public static final String attrFlags(byte attr) { "BGP4MSG_REFRESH" }; - public static final String bgpType(int bgpType) { + public static String bgpType(int bgpType) { try { return bgpTypes[bgpType]; } catch (Exception e) { @@ -159,19 +184,87 @@ public static final String bgpType(int bgpType) { protected static final SimpleDateFormat mrtFormat = new SimpleDateFormat("MM/dd/yy HH:mm:ss"); + /** * * @param InetAddress ia - * @return Textual representation of the InetAddress + * @param int afi + * @return boolean + * + * It looks if it is an ipv4 embedded in ipv6 + */ + public static boolean isInIpv4EmbeddedIpv6Format(InetAddress ia, int afi){ + if(ia instanceof Inet4Address && afi == AFI_IPv6){ + return true; + } + if (ia instanceof Inet6Address && ((Inet6Address)ia).isIPv4CompatibleAddress()) { + return true; + } + return false; + } + + /** + * + * @param byte[] base + * @return boolean + * + * It looks if it is an ipv4 embedded in ipv6 + */ + public static boolean isInIpv4EmbeddedIpv6Format(byte[] base) { + try { + InetAddress ia = InetAddress.getByAddress(base); + if (ia instanceof Inet4Address && base.length == IPv6_LENGTH) { + return true; + } + if (ia instanceof Inet6Address && ((Inet6Address)ia).isIPv4CompatibleAddress()) { + return true; + } + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + return false; + } + + /** + * + * @param InetAddress inetAddress + * @param boolean isIpv4EmbeddedIpv6 + * @return Textual representation of the InetAddress. + * It also parses if it is a ipv4 embedded in ip6 * * for all addresses, removes initial name */ - protected static final String ipAddressString(InetAddress ia) - { - return ia.getHostAddress(). - // replaceFirst("^[^/]*/", ""). - replaceFirst(":0(:0)+","::"). - replaceFirst("^0:",""). - replaceFirst(":::", "::"); + public static String ipAddressString(InetAddress inetAddress, boolean isIpv4EmbeddedIpv6) { + if (inetAddress == null) return ""; + + String ipAddressString = inetAddress.getHostAddress(); + try { + if (isIpv4EmbeddedIpv6) { + if(inetAddress instanceof Inet4Address) + ipAddressString = "::ffff:" + ipAddressString; + else if (inetAddress instanceof Inet6Address){ + byte[] ipv4Embedded = Arrays.copyOfRange(inetAddress.getAddress(), 12, IPv6_LENGTH); + String ipv4Str = (Inet4Address.getByAddress(ipv4Embedded)).getHostAddress(); + if(ipv4Str.equals("0.0.0.0")) ipv4Str =""; + ipAddressString = "::".concat(ipv4Str); + } + } + + if (ipAddressString.equals(":")) + ipAddressString = "::"; + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + + return ipAddressString; } + + public static String ipAddressString(byte[] ipAddressBase, boolean isIpv4EmbeddedIpv6) { + try { + return ipAddressString(InetAddress.getByAddress(ipAddressBase), isIpv4EmbeddedIpv6); + } catch (UnknownHostException e) { + e.printStackTrace(); + return "??/"; + } + } } diff --git a/src/org/javamrt/mrt/MRTRecord.java b/src/main/java/org/javamrt/mrt/MRTRecord.java similarity index 62% rename from src/org/javamrt/mrt/MRTRecord.java rename to src/main/java/org/javamrt/mrt/MRTRecord.java index 4cba92d..fd0ff1d 100644 --- a/src/org/javamrt/mrt/MRTRecord.java +++ b/src/main/java/org/javamrt/mrt/MRTRecord.java @@ -6,52 +6,44 @@ package org.javamrt.mrt; -import java.net.InetAddress; - import org.javamrt.utils.RecordAccess; +import java.net.InetAddress; +import java.util.Arrays; -public class MRTRecord { - protected byte[] record; - protected int type; - protected int subtype; - protected long time; - - public MRTRecord() { - } - protected MRTRecord(byte[] header) { - setHeaderData(header); - } +public class MRTRecord { + final protected byte[] header; + final protected byte[] body; - public void setGeneric(byte[] header, byte[] record) { - setHeaderData(header); - this.record = record; + protected MRTRecord(byte[] header, byte[] body) { + this.header = header; + this.body = body; } - public void setHeaderData(byte[] header) { - this.time = RecordAccess.getU32(header, 0); - this.type = RecordAccess.getU16(header, 4); - this.subtype = RecordAccess.getU16(header, 6); + public byte[] getBytes() { + byte[] result = Arrays.copyOf(header, header.length + body.length); + System.arraycopy(body, 0, result, header.length, body.length); + return result; } public long getTime() { - return this.time; + return RecordAccess.getU32(header, 0); } public int getType() { - return this.type; + return RecordAccess.getU16(header, 4); } public long getSubType() { - return this.subtype; + return RecordAccess.getU16(header, 6); } // will be overriden by derived classes public String toString() { return String - .format("MRT|%d|%d|%d", this.time, this.type, this.subtype); + .format("MRT|%d|%d|%d", getTime(), getType(), getSubType()); } public boolean hasAsPathPrepend() { diff --git a/src/main/java/org/javamrt/mrt/MalformedBgpStreamException.java b/src/main/java/org/javamrt/mrt/MalformedBgpStreamException.java new file mode 100644 index 0000000..163f552 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/MalformedBgpStreamException.java @@ -0,0 +1,18 @@ +package org.javamrt.mrt; + +import java.io.IOException; + +/** + * Thrown if the input stream of data is broken in a way that is not possible to read any more MRT records from it. + * Therefore you should not try to call BGPFileReader.readNext() on the same input anymore. + * All other exceptions are potentially local to the MRT record they are thrown for, so the readNext() should be possible. + */ +public class MalformedBgpStreamException extends IOException { + public MalformedBgpStreamException(Throwable e) { + super(e); + } + + public MalformedBgpStreamException(String s) { + super(s); + } +} diff --git a/src/org/javamrt/mrt/Med.java b/src/main/java/org/javamrt/mrt/Med.java similarity index 100% rename from src/org/javamrt/mrt/Med.java rename to src/main/java/org/javamrt/mrt/Med.java diff --git a/src/org/javamrt/mrt/MpReach.java b/src/main/java/org/javamrt/mrt/MpReach.java similarity index 85% rename from src/org/javamrt/mrt/MpReach.java rename to src/main/java/org/javamrt/mrt/MpReach.java index 066eb90..be9fba2 100644 --- a/src/org/javamrt/mrt/MpReach.java +++ b/src/main/java/org/javamrt/mrt/MpReach.java @@ -6,6 +6,7 @@ package org.javamrt.mrt; +import java.net.Inet4Address; import java.net.InetAddress; import java.util.Vector; @@ -20,6 +21,8 @@ public class MpReach implements Attribute { private int afi; private int safi; private InetAddress nextHop; + private byte[] nextHopBytes; + private boolean isInIpv4EmbeddedIpv6Format = false; private Vector nlriVector; // private int snpaNo; @@ -76,8 +79,14 @@ public class MpReach implements Attribute { nhl = 16; } - byte[] abuf = RecordAccess.getBytes(buffer,offset, nhl); - nextHop = InetAddress.getByAddress(abuf); + nextHopBytes = RecordAccess.getBytes(buffer,offset, nhl); + + + nextHop = InetAddress.getByAddress(nextHopBytes); + + + isInIpv4EmbeddedIpv6Format = MRTConstants.isInIpv4EmbeddedIpv6Format(nextHop, afi); + offset += nextHopLen; /** @@ -133,6 +142,10 @@ public class MpReach implements Attribute { } + public boolean getIsInIpv4EmbeddedIpv6Format(){ + return isInIpv4EmbeddedIpv6Format; + } + public Vector getNlri() { return nlriVector; } @@ -140,14 +153,19 @@ public Vector getNlri() { public InetAddress getNextHop() { return nextHop; } + public byte[] getNextHopBytes() { + return nextHopBytes; + } /* * public Vector < InetAddress > getSNPA () { return snpaVector; } */ public String toString() { - String result = "NEXT HOP: " + nextHop.getHostAddress(); - for (Nlri nlri : nlriVector) - result += "\n NRLI:" + nlri.toString(); - return result; + return MRTConstants.ipAddressString(nextHop, isInIpv4EmbeddedIpv6Format); + +// String result = "NEXT HOP: " + nextHop.getHostAddress(); +// for (Nlri nlri : nlriVector) +// result += "\n NLRI:" + nlri.toString(); + } } diff --git a/src/org/javamrt/mrt/MpUnReach.java b/src/main/java/org/javamrt/mrt/MpUnReach.java similarity index 100% rename from src/org/javamrt/mrt/MpUnReach.java rename to src/main/java/org/javamrt/mrt/MpUnReach.java diff --git a/src/org/javamrt/mrt/NextHop.java b/src/main/java/org/javamrt/mrt/NextHop.java similarity index 72% rename from src/org/javamrt/mrt/NextHop.java rename to src/main/java/org/javamrt/mrt/NextHop.java index 05579bc..516869d 100644 --- a/src/org/javamrt/mrt/NextHop.java +++ b/src/main/java/org/javamrt/mrt/NextHop.java @@ -13,6 +13,10 @@ public class NextHop implements Attribute { + protected InetAddress nextHopIA; + protected byte[] nextHopIABytes; + private boolean isInIpv4EmbeddedIpv6Format = false; + public NextHop() throws Exception { byte nh[] = { (byte) 255, (byte) 255, (byte) 255, (byte) 255 }; this.nextHopIA = InetAddress.getByAddress(nh); @@ -21,8 +25,9 @@ public NextHop() throws Exception { public NextHop(byte[] buffer) throws Exception { int offset = 0; int len = buffer.length; - byte nh[] = RecordAccess.getBytes(buffer, offset, len); - this.nextHopIA = InetAddress.getByAddress(nh); + nextHopIABytes = RecordAccess.getBytes(buffer, offset, len); + this.nextHopIA = InetAddress.getByAddress(nextHopIABytes); + isInIpv4EmbeddedIpv6Format = MRTConstants.isInIpv4EmbeddedIpv6Format(nextHopIABytes); } public NextHop(InetAddress nextHopIA) throws NullPointerException { @@ -32,14 +37,17 @@ public NextHop(InetAddress nextHopIA) throws NullPointerException { this.nextHopIA = nextHopIA; } - public InetAddress getRaw() { + public InetAddress getNextHopIA() { return nextHopIA; } + public byte[] getNextHopIABytes() { + return nextHopIABytes; + } public String toString() { if (this.nextHopIA == null) return null; - return MRTConstants.ipAddressString(nextHopIA); + return MRTConstants.ipAddressString(nextHopIA, isInIpv4EmbeddedIpv6Format); } public boolean isDefault() { @@ -59,6 +67,4 @@ public boolean equals(Object o) { return this.nextHopIA.equals(((NextHop) o).nextHopIA); return false; } - - protected InetAddress nextHopIA; } diff --git a/src/main/java/org/javamrt/mrt/Nlri.java b/src/main/java/org/javamrt/mrt/Nlri.java new file mode 100644 index 0000000..e14315f --- /dev/null +++ b/src/main/java/org/javamrt/mrt/Nlri.java @@ -0,0 +1,60 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import org.javamrt.progs.route_btoa; +import org.javamrt.utils.RecordAccess; + +//IPv4/IPv6 address and mask length extractor from NLRI field in BGP + +public class Nlri + extends Prefix { + // from Prefix.java + // byte[] addr; + // int maskLength; + + public Nlri (byte[]record, int offset, int afi) + throws Exception + + { + super(); + /* + Receives whole BGP update package as byte [] record with integer offset as indicator of the position of length field + integer afi is used for deciding the type of address + masklen value is inside length octet, where the value is given in bits + +---------------------------+ + | Length (1 octet) | + +---------------------------+ + | Prefix (variable) | + +---------------------------+ + */ + if (afi == MRTConstants.AFI_IPv4) + this.base = new byte[4]; //deciding the type or address + else if (afi == MRTConstants.AFI_IPv6) + this.base = new byte[16]; + else + throw new Exception("NLRI: unknown Address Family: " + afi); + this.maskLength = RecordAccess.getU8(record, offset); //reading length byte (bits) + if (afi == MRTConstants.AFI_IPv4 && maskLength > 32) { + route_btoa.System_err_println(String.format("Bit length %d is not feasible for IPv4 prefix (offset %d)%n", maskLength, offset)); + throw new BGPFileReaderException(String.format( + "Bit length %d is not feasible for IPv4 prefix (offset %d)", maskLength, offset), record); + } else if (afi == MRTConstants.AFI_IPv6 && maskLength > 128) { + route_btoa.System_err_println(String.format("Bit length %d is not feasible for IPv6 prefix (offset %d)%n", maskLength, offset)); + throw new BGPFileReaderException(String.format( + "Bit length %d is not feasible for IPv6 prefix (offset %d)", maskLength, offset), record); + } + offset++; + if (offset + nrBytes() > record.length) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Not enough input bytes (%s) to read NLRI prefix (%d bytes from offset %d)", + RecordAccess.arrayToString(record), nrBytes(), offset)); + } + System.arraycopy(record, offset, base, 0, nrBytes()); + setPrefix(this.base, this.maskLength); + } +} diff --git a/src/main/java/org/javamrt/mrt/Notification.java b/src/main/java/org/javamrt/mrt/Notification.java new file mode 100644 index 0000000..7a114c9 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/Notification.java @@ -0,0 +1,46 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import java.net.InetAddress; + +public class Notification + extends MRTRecord +{ + protected InetAddress peerIP = null; + protected AS peerAS = new AS(0); + + public Notification (byte[]header, byte[]record) + { + super(header, record); + } + + public Notification(byte[] header, byte[] record, InetAddress peerIP, AS peerAS) { + super(header, record); + this.peerIP = peerIP; + this.peerAS = peerAS; + } + + public AS getPeerAS() { + return this.peerAS; + } + public InetAddress getPeer() { + return this.peerIP; + } + + public String toString() { + String peerString = MRTConstants.ipAddressString(this.peerIP, false); + + StringBuilder result = new StringBuilder("NOTIFICATION").append('|') + .append(getTime()).append('|') + // this comes from MRTRecord + .append(peerString).append('|') + .append(this.peerAS); + + return result.toString(); + } +} diff --git a/src/main/java/org/javamrt/mrt/Open.java b/src/main/java/org/javamrt/mrt/Open.java new file mode 100644 index 0000000..8eb9e6f --- /dev/null +++ b/src/main/java/org/javamrt/mrt/Open.java @@ -0,0 +1,59 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.mrt; + +import org.javamrt.utils.Debug; + +import java.net.InetAddress; + +public class Open + extends MRTRecord +{ + protected InetAddress peerIP = null; + protected AS peerAS = new AS(0); + protected long bgpId; + + public Open(byte[] header, byte[] record, InetAddress peerIP, AS peerAS, long bgpId) { + super(header, record); + this.peerIP = peerIP; + this.peerAS = peerAS; + this.bgpId = bgpId; + } + + Open (byte[]header, byte[]record) + { + super (header, record); + + if (Debug.compileDebug) { + Debug.println("Open message"); + Debug.dump(header); + Debug.dump(record); + } + + } + + public AS getPeerAS() { + return this.peerAS; + } + public InetAddress getPeer() { + return this.peerIP; + } + public long getBgpId(){return this.bgpId;} + + public String toString() { + String peerString = MRTConstants.ipAddressString(this.peerIP, false); + + StringBuilder result = new StringBuilder("OPEN").append('|') + .append(getTime()).append('|') + // this comes from MRTRecord + .append(peerString).append('|') + .append(this.peerAS).append('|') + .append(this.bgpId); + + return result.toString(); + } +} diff --git a/src/org/javamrt/mrt/OriginatorID.java b/src/main/java/org/javamrt/mrt/OriginatorID.java similarity index 95% rename from src/org/javamrt/mrt/OriginatorID.java rename to src/main/java/org/javamrt/mrt/OriginatorID.java index 680461a..1c35943 100644 --- a/src/org/javamrt/mrt/OriginatorID.java +++ b/src/main/java/org/javamrt/mrt/OriginatorID.java @@ -16,7 +16,7 @@ public class OriginatorID implements Attribute { } public String toString() { - return "" + id; + return Long.toString(id); } public long originatorId() { diff --git a/src/org/javamrt/mrt/Prefix.java b/src/main/java/org/javamrt/mrt/Prefix.java similarity index 78% rename from src/org/javamrt/mrt/Prefix.java rename to src/main/java/org/javamrt/mrt/Prefix.java index e6078d9..ed25145 100644 --- a/src/org/javamrt/mrt/Prefix.java +++ b/src/main/java/org/javamrt/mrt/Prefix.java @@ -14,8 +14,7 @@ public class Prefix implements Comparable, Comparator { - private final static int Inet4AddrLen = 4; - private final static int Inet6AddrLen = 16; + protected boolean isInIpv4EmbeddedIpv6Format = false; // for Nlri.java protected byte[] base; @@ -26,22 +25,15 @@ public class Prefix implements Comparable, Comparator { // protected InetAddress broadcastAddress; protected int maskLength; - protected Prefix() { - //baseAddress = null; - //broadcastAddress = null; - this.base = null; - this.mask = null; - this.broadcast = null; - this.maskLength = 0; - } + protected Prefix() {/* for inheritance*/} public Prefix(InetAddress addr, int maskLength) - throws PrefixMaskException { + throws PrefixMaskException, UnknownHostException { setPrefix(addr.getAddress(), maskLength); } public Prefix(byte[] addr, int maskLength) - throws PrefixMaskException { + throws PrefixMaskException, UnknownHostException { setPrefix(addr, maskLength); } @@ -51,13 +43,13 @@ public static Prefix parseString(String s) throws NumberFormatException, Unknown } protected void setPrefix(byte[] addr, int maskLen) - throws PrefixMaskException { + throws PrefixMaskException, UnknownHostException { /* * v2.00 * Check before anything else * backported from org.paag.netkit v2.00 */ - if (addr.length != Inet4AddrLen && addr.length != Inet6AddrLen) + if (addr.length != MRTConstants.IPv4_LENGTH && addr.length != MRTConstants.IPv6_LENGTH) throw new PrefixMaskException(addr, maskLen); if (addr.length * 8 < maskLen) @@ -76,10 +68,7 @@ protected void setPrefix(byte[] addr, int maskLen) throw new PrefixMaskException(this.base, this.maskLength); } */ - for (int n = 0; n < this.mask.length; n++) { - this.base[n] = addr[n]; - this.mask[n] = 0; - } + System.arraycopy(addr, 0, this.base, 0, this.mask.length); for (int n = this.maskLength; n > 0; n--) { for (int i = 0; i < this.mask.length; i++) { byte carry = (byte) (this.mask[i] & 0x01); @@ -98,6 +87,8 @@ protected void setPrefix(byte[] addr, int maskLen) for (int i = 0; i < this.mask.length; i++) { this.broadcast[i] = (byte) (this.base[i] | ~this.mask[i]); } + + isInIpv4EmbeddedIpv6Format = MRTConstants.isInIpv4EmbeddedIpv6Format(this.base); } public InetAddress getBaseAddress() { @@ -108,6 +99,14 @@ public InetAddress getBaseAddress() { } } + public byte[] getByteBaseArrayAddress(){ + return this.base; + } + + public boolean getIsInIpv4EmbeddedIpv6Format(){ + return isInIpv4EmbeddedIpv6Format; + } + public InetAddress getBroadcastAddress() { try { return InetAddress.getByAddress(this.broadcast); @@ -123,8 +122,7 @@ public int getMaskLength() { public void setMaskLength(int maskLength) throws PrefixMaskException, UnknownHostException { byte[] temp= new byte[this.base.length]; - for (int i=0;i 0 ? 1 + (maskLength - 1) / 8 : 0; + } + + /** + * @return the number of bytes needed to represent this prefix, plus one + */ + public int getOffset() { + return 1 + nrBytes(); + } } diff --git a/src/org/javamrt/mrt/PrefixMaskException.java b/src/main/java/org/javamrt/mrt/PrefixMaskException.java similarity index 93% rename from src/org/javamrt/mrt/PrefixMaskException.java rename to src/main/java/org/javamrt/mrt/PrefixMaskException.java index 6fd52ee..e3e91d6 100644 --- a/src/org/javamrt/mrt/PrefixMaskException.java +++ b/src/main/java/org/javamrt/mrt/PrefixMaskException.java @@ -32,9 +32,10 @@ public PrefixMaskException(byte[] addr, int mask) } } - public String toString() { - return new String(description); - } + @Override + public String getMessage() { + return description; + } private static final long serialVersionUID = 1L; } diff --git a/src/org/javamrt/mrt/RFC4893.java b/src/main/java/org/javamrt/mrt/RFC4893.java old mode 100644 new mode 100755 similarity index 73% rename from src/org/javamrt/mrt/RFC4893.java rename to src/main/java/org/javamrt/mrt/RFC4893.java index 057a698..5a2d338 --- a/src/org/javamrt/mrt/RFC4893.java +++ b/src/main/java/org/javamrt/mrt/RFC4893.java @@ -6,6 +6,7 @@ package org.javamrt.mrt; +import org.javamrt.progs.route_btoa; import org.javamrt.utils.RecordAccess; @@ -29,9 +30,8 @@ public class RFC4893 { public static void replaceAS23456(byte[] buffer, ASPath aspath) throws RFC4893Exception, AttributeException, Exception { if (DEBUG) { - System.err.printf("ASPATH = %s\nAS4PATH attribute:\n", aspath - .toString()); - RecordAccess.dump(System.err, buffer); + route_btoa.System_err_println(String.format("ASPATH = %s\nAS4PATH attribute:", aspath.toString())); + RecordAccess.dump(buffer); } if (buffer.length == 0) @@ -48,14 +48,24 @@ public static void replaceAS23456(byte[] buffer, ASPath aspath) * for the AS4_PATH attribute. */ - for (AS as4:as4path.path) { - if (as4 instanceof ASConfedSet || as4 instanceof ASConfedSequence) - throw new RFC4893Exception(MRTConstants.asConfedSequence, aspath,as4path); + for (AS as4 : as4path.path) { + if (as4 instanceof ASConfedSet || as4 instanceof ASConfedSequence) { + if (BGPFileReader.isLenient()) { + route_btoa.System_err_println(String.format("RFC4893 violation with AS4PATH containing %s%n" + + " while trying to modify: %s%n" + + " with 4 byte ASPATH: %s", + MRTConstants.asPathString(MRTConstants.asConfedSequence), + aspath, + as4path)); + } else { + throw new RFC4893Exception(MRTConstants.asConfedSequence, aspath,as4path); + } + } } if (DEBUG) { - System.err.printf("as4path = %s\n", as4path); - System.err.printf("old ASPATH = %s\n", aspath); + route_btoa.System_err_println(String.format("as4path = %s", as4path)); + route_btoa.System_err_println(String.format("old ASPATH = %s", aspath)); } /* @@ -79,7 +89,7 @@ public static void replaceAS23456(byte[] buffer, ASPath aspath) aspath.getPath().subList(asElement, aspath.length()).clear(); aspath.getPath().addAll(as4path.getPath()); if (DEBUG) - System.err.printf("new ASPath = %s\n", aspath.toString()); + route_btoa.System_err_println(String.format("new ASPath = %s", aspath.toString())); aspath.mkPrependers(); // rebuild prepender list } diff --git a/src/org/javamrt/mrt/RFC4893Exception.java b/src/main/java/org/javamrt/mrt/RFC4893Exception.java similarity index 98% rename from src/org/javamrt/mrt/RFC4893Exception.java rename to src/main/java/org/javamrt/mrt/RFC4893Exception.java index 621c657..1709057 100644 --- a/src/org/javamrt/mrt/RFC4893Exception.java +++ b/src/main/java/org/javamrt/mrt/RFC4893Exception.java @@ -80,7 +80,8 @@ public ASPath getAS4Path() { return this.as4path; } - public String toString() { + @Override + public String getMessage() { if (peer == null || as == null) return String.format("RFC4893 violation @ %d: AS4PATH contains %s",this.timestamp,MRTConstants.asPathString(cause)); return String.format( diff --git a/src/org/javamrt/mrt/Refresh.java b/src/main/java/org/javamrt/mrt/Refresh.java similarity index 96% rename from src/org/javamrt/mrt/Refresh.java rename to src/main/java/org/javamrt/mrt/Refresh.java index 9af488d..6edd23c 100644 --- a/src/org/javamrt/mrt/Refresh.java +++ b/src/main/java/org/javamrt/mrt/Refresh.java @@ -41,7 +41,7 @@ public class Refresh public Refresh (byte[]header, byte[]record) { - super (header); + super (header, record); this.AFI = RecordAccess.getU16(record,0); this.SAFI = RecordAccess.getU8 (record,2); } @@ -49,7 +49,7 @@ public Refresh (byte[]header, byte[]record) public String toString () { return String.format("BGP4MP|%d|REFRESH|%d|%d", - this.time, + this.getTime(), this.AFI, this.SAFI); } diff --git a/src/org/javamrt/mrt/StateChange.java b/src/main/java/org/javamrt/mrt/StateChange.java old mode 100644 new mode 100755 similarity index 62% rename from src/org/javamrt/mrt/StateChange.java rename to src/main/java/org/javamrt/mrt/StateChange.java index c7605fc..c5ebcba --- a/src/org/javamrt/mrt/StateChange.java +++ b/src/main/java/org/javamrt/mrt/StateChange.java @@ -12,18 +12,22 @@ public class StateChange { public StateChange( - long time, - InetAddress gatewayIP, - AS gatewayAS, - int old_state, - int new_state) + long time, + InetAddress gatewayIP, + AS gatewayAS, + int old_state, + int new_state, + String updateStr, + byte[] header, byte[] record) { - // System.out.println("State Change"); + super(header, record); + // route_btoa.System_err_println("State Change"); this.time = time; this.gatewayIP = gatewayIP; this.gatewayAS = gatewayAS; this.old_state = old_state; this.new_state = new_state; + this.updateStr = updateStr; } private long time; @@ -33,6 +37,13 @@ public long getTime() } private InetAddress gatewayIP; + + public InetAddress getPeer() + { + return this.gatewayIP; + } + + @Deprecated public InetAddress getPeerIP() { return this.gatewayIP; @@ -56,15 +67,16 @@ public int getNewState() return this.new_state; } + private String updateStr = ""; public String toString() { - return String.format("BGP4MP|%d|STATE|%s|%s|%d|%d", + return String.format("%s|%d|STATE|%s|%s|%d|%d", + this.updateStr, this.time, - this.gatewayIP.getHostAddress(), + MRTConstants.ipAddressString(gatewayIP, false), this.gatewayAS.toString(), this.old_state, - this.new_state).replaceAll("(:0)+:0{0,1}","::"); - + this.new_state); } } diff --git a/src/org/javamrt/mrt/TableDump.java b/src/main/java/org/javamrt/mrt/TableDump.java old mode 100644 new mode 100755 similarity index 83% rename from src/org/javamrt/mrt/TableDump.java rename to src/main/java/org/javamrt/mrt/TableDump.java index fb4fbb3..70f2d0b --- a/src/org/javamrt/mrt/TableDump.java +++ b/src/main/java/org/javamrt/mrt/TableDump.java @@ -5,13 +5,15 @@ // http://www.gnu.org/licenses/lgpl-3.0-standalone.html package org.javamrt.mrt; + +import org.javamrt.progs.route_btoa; +import org.javamrt.utils.RecordAccess; + import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.Date; -import org.javamrt.utils.RecordAccess; - public class TableDump extends MRTRecord @@ -23,23 +25,26 @@ protected TableDump(int view, long origTime, InetAddress peer, AS peerAs, - Attributes attributes) + Attributes attributes, + byte[] header, + byte[] body) { + super(header, body); if (false) - System.out.printf("TableDump(int view =%d\n"+ - " int sequence = %d\n"+ - " Prefix prefix = %s\n"+ - " long origTime = %d\n"+ - " InetAddress peer = %s\n"+ - " AS peerAs = %s\n"+ - " Attributes attributes = %s);\n", - view, - sequence, - prefix, - origTime, - peer, - peerAs, - attributes); + route_btoa.System_err_println(String.format("TableDump(int view =%d\n" + + " int sequence = %d\n" + + " Prefix prefix = %s\n" + + " long origTime = %d\n" + + " InetAddress peer = %s\n" + + " AS peerAs = %s\n" + + " Attributes attributes = %s);\n", + view, + sequence, + prefix, + origTime, + peer, + peerAs, + attributes)); this.view = view; this.sequence = sequence; @@ -56,7 +61,7 @@ protected TableDump(int view, TableDump (byte[]cabecera, byte[]record,int subtype) throws Exception { - super(cabecera); + super(cabecera, record); int decodeOffset = 0; int addrSize = (subtype == MRTConstants.AFI_IPv4) ? 4 : 16; @@ -126,7 +131,7 @@ public String toString () if (dumpString != null) return dumpString.toString (); String cleanPeer = // this.Peer.getHostAddress ().replaceFirst("(:0){2,7}", ":").replaceFirst("^0::", "::"); - MRTConstants.ipAddressString(this.Peer); + MRTConstants.ipAddressString(this.Peer, false); dumpString = new StringBuffer (this.type+"|" + this.origTime); dumpString.append ("|B|" + cleanPeer +"|"); diff --git a/src/org/javamrt/mrt/TableDumpv2.java b/src/main/java/org/javamrt/mrt/TableDumpv2.java similarity index 90% rename from src/org/javamrt/mrt/TableDumpv2.java rename to src/main/java/org/javamrt/mrt/TableDumpv2.java index 11c702b..69e7417 100644 --- a/src/org/javamrt/mrt/TableDumpv2.java +++ b/src/main/java/org/javamrt/mrt/TableDumpv2.java @@ -24,7 +24,9 @@ protected TableDumpv2(int view, long origTime, InetAddress peer, AS peerAs, - Attributes attributes) + Attributes attributes, + byte[] header, + byte[] body) { super(view, sequence, @@ -32,7 +34,9 @@ protected TableDumpv2(int view, origTime, peer, peerAs, - attributes); + attributes, + header, + body); this.type = "TABLE_DUMP2"; } diff --git a/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java b/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java new file mode 100644 index 0000000..3b7cfc8 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java @@ -0,0 +1,40 @@ +package org.javamrt.mrt; + +import java.util.Arrays; + +public class UnsupportedAttribute implements Attribute { + + private final int type; + private final byte[] buffer; + + public UnsupportedAttribute(int type, byte[] buffer) { + this.type = type; + this.buffer = buffer; + } + + @Override + public String toString() { + return "UnsupportedAttribute{" + + "type=" + type + + ", buffer=" + Arrays.toString(buffer) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UnsupportedAttribute that = (UnsupportedAttribute) o; + + if (type != that.type) return false; + return Arrays.equals(buffer, that.buffer); + } + + @Override + public int hashCode() { + int result = type; + result = 31 * result + Arrays.hashCode(buffer); + return result; + } +} diff --git a/src/org/javamrt/mrt/Withdraw.java b/src/main/java/org/javamrt/mrt/Withdraw.java similarity index 77% rename from src/org/javamrt/mrt/Withdraw.java rename to src/main/java/org/javamrt/mrt/Withdraw.java index 75267ca..2a62f27 100644 --- a/src/org/javamrt/mrt/Withdraw.java +++ b/src/main/java/org/javamrt/mrt/Withdraw.java @@ -11,11 +11,13 @@ public class Withdraw extends Bgp4Update { public Withdraw(byte[] header, + byte[] record, InetAddress peerIP, - AS peerAS, - Prefix prefix) + AS peerAS, + Prefix prefix, + String updateStr) { - super(header, peerIP, peerAS, prefix); + super(header, record, peerIP, peerAS, prefix, updateStr); this.updateType = 'W'; } diff --git a/src/org/javamrt/mrt/genericRib.java b/src/main/java/org/javamrt/mrt/genericRib.java old mode 100644 new mode 100755 similarity index 71% rename from src/org/javamrt/mrt/genericRib.java rename to src/main/java/org/javamrt/mrt/genericRib.java index 0c0285a..792aa3b --- a/src/org/javamrt/mrt/genericRib.java +++ b/src/main/java/org/javamrt/mrt/genericRib.java @@ -13,52 +13,52 @@ public class genericRib extends MRTRecord { - public genericRib () + public genericRib(byte[] header, byte[] body) { - super (); + super(header, body); /* int offset = 0; - long sequenceNo = RecordAccess.getU32 (this.record, offset); + long sequenceNo = RecordAccess.getU32 (this.body, offset); offset += 4; - int afi = RecordAccess.getU16 (this.record, offset); + int afi = RecordAccess.getU16 (this.body, offset); offset += 2; - int safi = RecordAccess.getU8 (this.record, offset); + int safi = RecordAccess.getU8 (this.body, offset); try { - nlri = new Nlri (this.record, offset, afi); + nlri = new Nlri (this.body, offset, afi); } catch (UnknownHostException e) { - e.printStackTrace (); + route_btoa.printStackTrace(e); } offset += nlri.getOffset (); - int entryCount = RecordAccess.getU16 (this.record, offset); + int entryCount = RecordAccess.getU16 (this.body, offset); / * try { stream=new BufferedInputStream(new FileInputStream("temp.xxx")); } catch (FileNotFoundException e1) { - e1.printStackTrace(); + route_btoa.printStackTrace(e1); } try { body=new byte [stream.available()]; int read=stream.read(body, 0, stream.available()); } catch (IOException e) { - e.printStackTrace(); + route_btoa.printStackTrace(e); } IndexTable test=new IndexTable(body); * / for (int i = 0; i < entryCount; i++) { - int peerIndex = RecordAccess.getU16 (this.record, offset); + int peerIndex = RecordAccess.getU16 (this.body, offset); offset += 2; - long time = RecordAccess.getU32 (this.record, offset); + long time = RecordAccess.getU32 (this.body, offset); offset += 4; - int attrLen = RecordAccess.getU16 (this.record, offset); + int attrLen = RecordAccess.getU16 (this.body, offset); offset += 2; GetAttributes bgp4Attributes = - new GetAttributes (record, attrLen, offset); + new GetAttributes (body, attrLen, offset); Vector attributes = bgp4Attributes.getAttributes (); StringBuffer attr = new StringBuffer (); @@ -86,5 +86,5 @@ public String toString () // private final int RIB_IP6_AFI=2; // private Nlri nlri; // private byte[] header; - // private byte[] record; + // private byte[] body; } diff --git a/src/org/javamrt/mrt/peer.java b/src/main/java/org/javamrt/mrt/peer.java similarity index 100% rename from src/org/javamrt/mrt/peer.java rename to src/main/java/org/javamrt/mrt/peer.java diff --git a/src/main/java/org/javamrt/progs/route_btoa.java b/src/main/java/org/javamrt/progs/route_btoa.java new file mode 100755 index 0000000..e9dc1f0 --- /dev/null +++ b/src/main/java/org/javamrt/progs/route_btoa.java @@ -0,0 +1,256 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.progs; + +import org.javamrt.mrt.*; +import org.javamrt.utils.Debug; +import org.javamrt.utils.getopts; + +import java.io.*; +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class route_btoa { + + public static Prefix prefix = null; + public static InetAddress peer = null; + public static AS originator = null; + public static AS traverses = null; + public static boolean showIPv4 = true; + public static boolean showIPv6 = true; + public static boolean printRFC4893violations = false; + public static String outputToFileExt = ""; + public static boolean outputErrToBuilder = false; + public static StringBuilder errBuilder = new StringBuilder(); + public static boolean callSystemExit = true; + + public static void main(String args[]) { + BGPFileReader in; + MRTRecord record; + getopts prueba; + + if (Debug.compileDebug) + prueba = new getopts(args, "46Dhmf:P:p:o:t:v"); + else + prueba = new getopts(args, "46hmf:P:p:o:v:t:"); + + char opcion; + + boolean oldall = false; + + while ((opcion = prueba.nextOption()) != 0) { + try { + switch (opcion) { + case '4': + if (showIPv4 == true && showIPv6 == true) + showIPv6 = false; + else + exit(usage(1)); + break; + + case '6': + if (showIPv4 == true && showIPv6 == true) + showIPv4 = false; + else + exit(usage(1)); + break; + case 'D': + Debug.setDebug(true); + break; + case 'm': + oldall = true; + break; + case 'p': + peer = InetAddress.getByName(prueba.optarg); + break; + + case 'P': + prefix = Prefix.parseString(prueba.optarg); + break; + + case 'v': + printRFC4893violations = true; + break; + + case 'o': + originator = AS.parseString(prueba.optarg); + break; + case 't': + traverses = AS.parseString(prueba.optarg); + break; + case 'f': + outputToFileExt = prueba.optarg; + break; + + case 'h': + default: + exit(usage((opcion) == 'h' ? 0 : 1)); + break; + } + } catch (UnknownHostException e) { + printStackTrace(e); + exit(1); + } catch (PrefixMaskException e) { + printStackTrace(e); + exit(1); + } catch (Exception e) { + printStackTrace(e); + exit(1); + } + } + + if (args.length == prueba.optind) { + exit(usage(0)); + } + + FileWriter out; + for (int arg = prueba.optind; arg < args.length; arg++) { + out = null; + try { + in = new BGPFileReader(args[arg]); + if (!outputToFileExt.equals("")) { + out = new FileWriter(args[arg] + + (outputToFileExt.startsWith(".") ? outputToFileExt : "." + outputToFileExt)); + } + int cnt=0; + while (false == in.eof()) { + try { + if ((record = in.readNext()) == null) + break; + ++cnt; + if (record instanceof Open + || record instanceof KeepAlive + || record instanceof Notification) + continue; + if (record instanceof StateChange) { + if (oldall == true && checkPeer(record)) { + outputData(record.toString(), out); + continue; + } + } + if ((record instanceof TableDump) + || (record instanceof Bgp4Update)) { + + if (!showIPv4 && record.getPrefix().isIPv4()) + continue; + if (!showIPv6 && record.getPrefix().isIPv6()) + continue; + try { + if (!checkPrefix(record)) + continue; + if (!checkPeer(record)) + continue; + if (!checkASPath(record)) + continue; + } catch (Exception e) { + printStackTrace(e); + route_btoa.System_err_println(String.format("record = %s", record)); + } + outputData(record.toString(), out); + } + } catch (RFC4893Exception rfce) { + if (printRFC4893violations) + route_btoa.System_err_println(rfce.toString()); + } catch (Exception e) { + route_btoa.System_err_println("Failed on record [" + cnt + "]"); + printStackTrace(e); + } + } + in.close(); + if (out != null) { + out.close(); + } + } catch (java.io.FileNotFoundException e) { + route_btoa.System_err_println("File not found: " + args[arg]); + } catch (Exception ex) { + route_btoa.System_err_println("Exception caught when reading " + args[arg]); + printStackTrace(ex); + } + } // for (int arg... + } // main() + + + private static int usage(int retval) { + PrintStream ps = System.err; + + ps.println("route_btoa f1 ..."); + ps.println(" -h print this help message"); + ps.println(" -m legacy compatibility wth MRT: include all records"); + ps.println(" -4 print IPv4 prefixes only"); + ps.println(" -6 print IPv6 prefixes only"); + ps.println(" -f ext writes the data to a file with extension e.g. .txt or txt"); + ps.println(" -p peer print updates from a specific peer only"); + ps.println(" -P prefix print updates for a specific prefix only"); + if (Debug.compileDebug) + ps.println(" -D enable debugging"); + ps.println(" -o as print updates generated by one AS only"); + ps.println(" -t as print updates where AS is in ASPATH"); + ps.println(" -4 and -6 together are not allowed"); + ps.println(" f1 ... are filenames or URL's"); + ps.println(" Use URL's according to the server's policies"); + ps.println(" Only prints records in machine readable format\n"); + + return retval; + } + + private static boolean checkPrefix(MRTRecord mrt) { + if (prefix == null) + return true; + return prefix.equals(mrt.getPrefix()); + } + + private static boolean checkPeer(MRTRecord mrt) { + if (peer == null) + return true; + return peer.equals(mrt.getPeer()); + } + + private static boolean checkASPath(MRTRecord mrt) { + if (originator == null) { + if (traverses == null) + return true; + // + // check whether AS is traversed by the prefix + // + return mrt.getASPath().contains(traverses); + } + // + // check whether the AS originates the prefix + // + return originator.equals(mrt.getASPath().generator()); + } + + private static void outputData(String data, FileWriter output) throws IOException { + if (output != null) { + output.write(data + System.lineSeparator()); + } else { + System.out.println(data); + } + } + + public static void exit(int exitCode) { + if (callSystemExit) { + System.exit(exitCode); + } + } + + public static void System_err_println(String str) { + if (outputErrToBuilder) { + errBuilder.append(str).append(System.lineSeparator()); + } else { + System.out.flush(); + System.err.println(str); + System.err.flush(); + } + } + + public static void printStackTrace(Throwable t) { + StringWriter errors = new StringWriter(); + t.printStackTrace(new PrintWriter(errors)); + System_err_println(errors.toString()); + } +} diff --git a/src/org/javamrt/utils/Common.java b/src/main/java/org/javamrt/utils/Common.java old mode 100644 new mode 100755 similarity index 89% rename from src/org/javamrt/utils/Common.java rename to src/main/java/org/javamrt/utils/Common.java index f9b49b7..1de2298 --- a/src/org/javamrt/utils/Common.java +++ b/src/main/java/org/javamrt/utils/Common.java @@ -7,6 +7,9 @@ package org.javamrt.utils; // import java.awt.GraphicsEnvironment; + +import org.javamrt.progs.route_btoa; + import java.util.Calendar; import java.util.TimeZone; @@ -148,14 +151,14 @@ public static void main(String args[]) { cal.set(Calendar.MILLISECOND,0); printCal(cal,"UTC"); - System.out.printf(" son %d segundos de la epoca\n",cal.getTimeInMillis()/1000L); + route_btoa.System_err_println(String.format(" son %d segundos de la epoca", cal.getTimeInMillis() / 1000L)); } private static void printCal(Calendar cal2, String zone) { - System.out.printf("%s: %2d/%02d/%04d %2d:%02d:%02d", zone, cal2 - .get(Calendar.DAY_OF_MONTH), cal2.get(Calendar.MONTH)+1, cal2 - .get(Calendar.YEAR), cal2.get(Calendar.HOUR_OF_DAY), cal2 - .get(Calendar.MINUTE), cal2.get(Calendar.SECOND)); + route_btoa.System_err_println(String.format("%s: %2d/%02d/%04d %2d:%02d:%02d", zone, cal2 + .get(Calendar.DAY_OF_MONTH), cal2.get(Calendar.MONTH) + 1, cal2 + .get(Calendar.YEAR), cal2.get(Calendar.HOUR_OF_DAY), cal2 + .get(Calendar.MINUTE), cal2.get(Calendar.SECOND))); } } diff --git a/src/org/javamrt/utils/Debug.java b/src/main/java/org/javamrt/utils/Debug.java similarity index 100% rename from src/org/javamrt/utils/Debug.java rename to src/main/java/org/javamrt/utils/Debug.java diff --git a/src/org/javamrt/utils/InetAddressComparator.java b/src/main/java/org/javamrt/utils/InetAddressComparator.java similarity index 100% rename from src/org/javamrt/utils/InetAddressComparator.java rename to src/main/java/org/javamrt/utils/InetAddressComparator.java diff --git a/src/org/javamrt/utils/LongOpt.java b/src/main/java/org/javamrt/utils/LongOpt.java similarity index 100% rename from src/org/javamrt/utils/LongOpt.java rename to src/main/java/org/javamrt/utils/LongOpt.java diff --git a/src/main/java/org/javamrt/utils/RecordAccess.java b/src/main/java/org/javamrt/utils/RecordAccess.java new file mode 100755 index 0000000..d77a4dd --- /dev/null +++ b/src/main/java/org/javamrt/utils/RecordAccess.java @@ -0,0 +1,122 @@ +// This file is part of java-mrt +// A library to parse MRT files + +// This file is released under LGPL 3.0 +// http://www.gnu.org/licenses/lgpl-3.0-standalone.html + +package org.javamrt.utils; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; + +public class RecordAccess +{ + + static public long getU32 (byte[] buffer,int offset) + { + return getUINT(buffer, offset, 4); + } + + static public long getUINT (byte[]buffer, int offset,int size) { + if (offset > buffer.length - size) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Not enough bytes to read %d bytes from offset %d in %s", size, offset, arrayToString(buffer))); + } + return new BigInteger(1, Arrays.copyOfRange(buffer, offset, offset + size)).longValue(); + } + + static public int getU16 (byte[]buffer, int offset) + { + if (offset > buffer.length - 2) { + throw new ArrayIndexOutOfBoundsException("Not enough bytes to read U16 from offset " + offset + " in " + arrayToString(buffer)); + } + return ((buffer[offset] & 0xff) << 8) | (buffer[offset+1] & 0xff); + } + + static public int getU8 (byte[]buffer, int offset) + { + return ((int) buffer[offset] & 0x000000ff); + } + + static public byte[] getBytes (byte[]buffer, int offset, int length) + { + if (buffer.length < offset + length) { + throw new ArrayIndexOutOfBoundsException(String.format( + "Not enough bytes to read %d bytes from offset %d in %s", length, offset, arrayToString(buffer))); + } + return Arrays.copyOfRange(buffer, offset, offset + length); + } + + static public String arrayToString(byte[] buffer) + { + final int offset = Math.max(0, buffer.length - 128); + return arrayToString(buffer, offset, buffer.length - offset); + } + + static public String arrayToString(byte[] buffer,int offset,int len) + { + StringBuilder result = new StringBuilder(); + + + for (int i=offset - (offset % 8);i < offset+len;i++) + { + if (i % 8 == 0) + result.append(String.format("\n%4d\t",i)); + if (i >= offset) + result.append(String.format("%02X ",buffer[i])); + else + result.append(" "); + } + result.append("\n"); + + return result.toString(); + } + + static public void dump(java.io.PrintStream out,byte[] buffer,int offset,int len) + { + out.print(arrayToString(buffer,offset,len)); + } + + static public void dump(byte[] buffer,int offset,int len) + { + dump(System.err,buffer,offset,len); + } + + static public void dump(java.io.PrintStream out,byte[] buffer,int len) + { + out.print(arrayToString(buffer,0,len)); + } + + static public void dump(byte[] buffer,int len) + { + dump(System.err,buffer,len); + } + + static public void dump(java.io.PrintStream out,byte[] buffer) + { + out.printf("buffer is %d bytes long", buffer.length); + out.print(arrayToString(buffer,0,buffer.length)); + } + + static public void dump(byte[] buffer) + { + dump(System.err,buffer,buffer.length); + } + + public static long getUINT(ByteBuffer buffer, int size) { + byte[] bytes = new byte[size]; + buffer.get(bytes); + return new BigInteger(1, bytes).longValue(); + } + + public static int getU16(ByteBuffer buffer) { + return buffer.getShort() & 0xFFFF; + } + + public static byte[] getBytes(ByteBuffer buffer, int size) { + byte[] bytes = new byte[size]; + buffer.get(bytes); + return bytes; + } +} diff --git a/src/org/javamrt/utils/getopts.java b/src/main/java/org/javamrt/utils/getopts.java old mode 100644 new mode 100755 similarity index 87% rename from src/org/javamrt/utils/getopts.java rename to src/main/java/org/javamrt/utils/getopts.java index 2ee25ed..89559ea --- a/src/org/javamrt/utils/getopts.java +++ b/src/main/java/org/javamrt/utils/getopts.java @@ -6,7 +6,10 @@ package org.javamrt.utils; +import org.javamrt.progs.route_btoa; + import java.io.PrintStream; +import java.util.Arrays; /** * Parse command line options the **IX way
@@ -165,9 +168,7 @@ public int optarg() { public static String arg = null; public static void main(String[] args) { - for (int i=0; i: con argumento "), new LongOpt('b',"beg", false," : sin argumento"), @@ -177,27 +178,27 @@ public static void main(String[] args) { while ((opcion = test.nextOption()) > 0) { switch (opcion){ case 'a': - System.out.println("option ["+opcion+"]("+test.optopt+") argument: "+test.optarg); + route_btoa.System_err_println("option ["+opcion+"]("+test.optopt+") argument: "+test.optarg); break; case 'b': - System.out.println("short and long without option: ["+opcion+"]"+test.optopt); + route_btoa.System_err_println("short and long without option: ["+opcion+"]"+test.optopt); break; case 'c': - System.out.println("Special short only option ["+opcion+"]"+test.optopt); + route_btoa.System_err_println("Special short only option ["+opcion+"]"+test.optopt); break; case 255: - System.out.println("Missing argument for "+test.opterr); - System.exit(1); + route_btoa.System_err_println("Missing argument for "+test.opterr); + route_btoa.exit(1); default: - System.err.println("Error processing "+args[test.optind]); - System.exit(1); + route_btoa.System_err_println("Error processing "+args[test.optind]); + route_btoa.exit(1); } } for (int arg=test.optind; arg asList; - - public ASConfedSequence(LinkedList asList) { - this.asList = new LinkedList(); - this.asList.addAll(asList); - } - - public String toString() { - String result = "["; - for (AS as:asList) - result = result.concat(" "+as.toString()); - return result.concat(" ]"); - } -} diff --git a/src/org/javamrt/mrt/ASConfedSet.java b/src/org/javamrt/mrt/ASConfedSet.java deleted file mode 100644 index 082cd82..0000000 --- a/src/org/javamrt/mrt/ASConfedSet.java +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -import java.util.LinkedList; - -public class ASConfedSet extends AS { - - private LinkedList asList; - - public ASConfedSet(LinkedList asList) { - this.asList = new LinkedList(); - this.asList.addAll(asList); - } - - - public String toString() { - String result = "{"; - for (AS as:asList) - result = result.concat(" "+as.toString()); - return result.concat(" }"); - } -} - diff --git a/src/org/javamrt/mrt/ASPath.java b/src/org/javamrt/mrt/ASPath.java deleted file mode 100644 index c0656d6..0000000 --- a/src/org/javamrt/mrt/ASPath.java +++ /dev/null @@ -1,320 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -import java.util.LinkedList; -import java.util.Vector; - -import org.javamrt.utils.RecordAccess; - - -/** - * - * ASPath is a linked list of elements which are either
- * AS, ASSet or ASConfedSet - *

- * version 3.00: complete rewrite with ASPathSegment
- * - * @version 3.00 - * @author paag - */ -public class ASPath implements Attribute { - protected LinkedList path; - - ASPath() { - this.path = new LinkedList(); - this.prependers = null; - } - - ASPath(byte[] buffer) throws Exception { - decode(buffer, 2); - } - - ASPath(byte[] buffer, int asSize) throws Exception { - decode(buffer, asSize); - } - - - private void decode(byte[] buffer, int asSize) throws Exception { - this.path = new LinkedList(); - int offset = 0; - // System.err.printf("New ASPATH (%d bytes)\n",buffer.length); - while (offset < buffer.length) { - ASPathSegment segment = new ASPathSegment(buffer, offset, asSize); - // System.err.printf(" segment @%-2d [t %2d]: %s\n",offset,segment.bType(),segment.toString()); - switch (segment.bType()) { - case MRTConstants.asSequence: - this.path.addAll(segment.getASList()); - break; - case MRTConstants.asSet: - if (null != segment.getAS()) - this.path.add(segment.getAS()); - else - this.path.add(new ASSet(segment.getASList())); - break; - case MRTConstants.asConfedSequence: - this.add(new ASConfedSequence(segment.getASList())); - break; - case MRTConstants.asConfedSet: - this.add(new ASConfedSet(segment.getASList())); - break; - default: - RecordAccess.dump(buffer); - throw new Exception( - String.format("Unknown ASPATH Segment Type = %d Len = %d", - segment.bType(), segment.bLen()) - ); - } - offset+=segment.bLen(); - } - // - // and now try to see if we have AS PATH Prepend - // - mkPrependers(); - // System.err.println("New ASPATH:"+path.toString()); - } - - public AS get(int i) { - try { - return path.get(i); - } catch (IndexOutOfBoundsException iobe) { - // - } - return null; - } - - public void set(int index,AS element) { - path.set(index, element); - } - /** - * Append an AS to the ASPATH - * @param as - */ - public void append(AS as) { - add(as); - } - - /** - * alias of append(AS as) - * @param as - */ - public void add(AS as) { - path.add(as); - refreshPrependers(as); - } - - public LinkedList getPath() { - return this.path; - } - /** - * - * @return the number of hops of the ASPATH - */ - public int length() { - return path.size(); - } - - /** - * @author paag - * @return a Vector with the AS's which are doing prepending. - */ - public Vector getPrependers() { - return prependers; - } - - /** - * - * @return - */ - public boolean hasAsPathPrepend() { - return prependers != null; - } - - /** - * @author paag - * rebuild the PREPENDER list - *
Called from decode() or when the AS4PATH attribute is decoded - */ - public void mkPrependers() { - int asPathLen; - - this.prependers = null; - if ((asPathLen = this.path.size()) == 0) - return; - for (int i=0; i(); - prependers.add(ahora); - i = this.path.lastIndexOf(ahora); - } - } - } - /** - * refresh the prependers list when an AS is appended to the ASPATH - * @param ultimo the AS which was appended to the ASPATH - * @author paag - */ - private void refreshPrependers(AS ultimo) { - // - // is the argument involved in AS PATH prepending? - // - if (this.path.indexOf(ultimo) == this.path.lastIndexOf(ultimo)) - return; - try { - // - // did we already register AS PATH prepending for the argument? - // - if (prependers.contains(ultimo)) - return; - } catch (Exception e) { - // - // no prependers yet - // - prependers = new Vector(); - } - prependers.add(ultimo); - } - - /** - * @param as: an AS - * @return the index of the first occurrence of AS in the ASPATH - */ - public int indexOf(AS as) { - return this.path.indexOf(as); - } - - /** - * @param as: an AS - * @return the index of the last occurrence of AS in the ASPATH - */ - public int lastIndexOf(AS as) { - return this.path.lastIndexOf(as); - } - - public boolean contains(AS as) { - return this.path.indexOf(as) != -1; - } - /** - * @author paag - * @return a textual representation of the ASPATH - */ - public String toString() { - try { - if (this.path.size() == 0) - return ""; - - String result = null; - for (int i = 0; i < this.path.size(); i++) - try { - result = result.concat(" "+this.path.get(i).toString()); - } catch (Exception e) { - result = this.path.get(i).toString(); - } - return result; - }catch (Exception e) { - return ""; - } - } - - /** - * Shortcut for equals(Object o) - * @param ASPath other: an other ASPath - * @return true if both have the same length and all AS's in them are in the same place. - * @author paag - */ - public boolean equals(ASPath other) { - if (this.path.size() != other.path.size()) - return false; - - for (int i = 0; i < this.path.size(); i++) - if (!this.path.get(i).equals(other.path.get(i))) - return false; - - return true; - } - - /** - * This is the canonical implementation of the equals() method - * @param Object o - *
- */ - public boolean equals(Object o) { - if (o == null) - return false; - if (o == this) - return true; - if (o instanceof ASPath) - return this.equals((ASPath)o); - return false; - } - - /** - * - * @param as: as Autonomous System - * @return true if as generated the prefix - */ - public boolean isGenerator(AS as) { - return as.equals(this.path.getLast()); - } - - /** - * - * @return the As which generated the prefix - */ - public AS generator() { - try { - return this.path.getLast(); - } catch (Exception e) { - return AS.NullAS; - } - } - /** - * Build a copy of the AS_PATH but without prepends - * @param none - * @author paag - */ - - public ASPath canonicalPath() { - ASPath canonical = new ASPath(); - - for (int i = 0; i < this.path.size(); i++) { - AS as = this.path.get(i); - canonical.add(as); - i = this.path.lastIndexOf(as); - } - return canonical; - } - - private Vector prependers; - - /** - * - * @return true if the originating AS is prepending - */ - public boolean hasOriginPrepend() { - try { - return this.prependers.contains(this.generator()); - } catch (NullPointerException npe) { - return false; - } - } - - public int compareTo(ASPath aspath) { - int result = 0; - if (this.equals(aspath)) return 0; - if (this.length() != aspath.length()) - return this.length() > aspath.length() ? 1 : -1; - for (int i=0; i < this.length(); i++) { - result = this.get(i).compareTo(aspath.get(i)); - if (result == 0) - return result; - } - return result; - } -} diff --git a/src/org/javamrt/mrt/BGPFileReader.java b/src/org/javamrt/mrt/BGPFileReader.java deleted file mode 100644 index 69ddec6..0000000 --- a/src/org/javamrt/mrt/BGPFileReader.java +++ /dev/null @@ -1,715 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.util.LinkedList; -import java.util.zip.GZIPInputStream; - -import org.javamrt.utils.Debug; -import org.javamrt.utils.RecordAccess; - -public class BGPFileReader { - private static final boolean debug = false; - - private BufferedInputStream in = null; - private LinkedList recordFifo; - - private boolean eof = false; - - private byte[] header; - private byte[] record; - - private String toString; - /***** - * - * public BGPFileReader (BufferedInputStream in) - * - * create a new BGPFileReader from BufferedInputStream - */ - - public BGPFileReader(BufferedInputStream in) { - this.in = in; - this.toString = in.toString(); - this.recordFifo = new LinkedList(); - this.header = new byte[12]; // always 12 bytes, create once - this.record = null; - this.eof = false; - } - - /***** - * - * public BGPFileReader (String name) - * - * create a new BGPFileReader from BufferedInputStream specified by the - * String name - * @throws Exception - */ - - public BGPFileReader(String name) throws Exception { - InputStream inStream = null; - this.toString = name; - - try { // to open the name as a URL - java.net.URL url = new java.net.URL(name); - inStream = url.openStream(); - } catch (java.net.MalformedURLException e) { - File inFile = new File(name); - if (!inFile.exists()) - throw new java.io.FileNotFoundException(name); - inStream = new FileInputStream(inFile); - } - - - if (this.toString.endsWith(".gz")) { - this.in = new BufferedInputStream(new GZIPInputStream(inStream)); - } else { - this.in = new BufferedInputStream(inStream); - } - - this.recordFifo = new LinkedList(); - this.header = new byte[12]; // always 12 bytes, create once - this.record = null; - this.eof = false; - } - - public BGPFileReader(File f) throws IOException { - if (!f.exists()) - throw new java.io.FileNotFoundException(); - FileInputStream inStream = new FileInputStream(f); - this.toString = f.getCanonicalPath(); - if (this.toString.endsWith(".gz")) { - this.in = new BufferedInputStream(new GZIPInputStream(inStream)); - } else { - this.in = new BufferedInputStream(inStream); - } - this.recordFifo = new LinkedList(); - this.header = new byte[12]; // always 12 bytes, create once - this.record = null; - this.eof = false; - } - - /*** - * void close() - * - * close the BGPFileReader - */ - public void close() throws java.io.IOException { - this.in.close(); - this.recordFifo.clear(); - this.recordFifo = null; - this.header = null; - this.record = null; - } - - /** - * toString(): return the name of the input Stream - */ - public String toString() { - return this.toString; - } - /*** - * - * MRTRecord readNext() - * - * returns next record on successful completion null on EOF - * - * throws Exception when something goes wrong - */ - - private int recordlen = 0; - private int type = 0; - private int subtype = 0; - private long time = 0; - - private long recordCounter = 0; - - /** - * @return the number of MRT binary format records read.
- * In the new MRT record formats, that has little or nothing
- * to do with the number of BGP Events read, since a record might
- * hold several BGP events. We need this in our quest for soft session
- * restarts. - */ - public long mrtRecords() { - return recordCounter; - } - - /** - * The name MRT record is not perfect, because actually it's routing events we get - * @return a BGP event or a BGP control message. - * @throws Exception - */ - public MRTRecord readNext() throws Exception { - MRTRecord result = null; - while (true) { - /* - * Consume any records waiting in the queue - * - * using recordFifo.add(MRTRecord) <=> recordFifo.remove() - */ - if (recordFifo.size() != 0) - return recordFifo.remove(); - - /* - * Help GC - */ - if (record != null) - record = null; - /* - * if the queue is empty, read from the file - */ - - int leidos = this.in.read(header, 0, header.length); - recordCounter ++; - /* - * EOF - */ - if (leidos <= 0) { - this.eof = true; - return null; - } - /* - * truncated - */ - if (leidos != this.header.length) { - this.eof = true; - throw new BGPFileReaderException("Truncated file: " + leidos - + " instead of " + this.header.length + " bytes", header); - } - if (Debug.compileDebug) - RecordAccess.dump(Debug.debugStream, header); - time = RecordAccess.getU32(header, 0); - type = RecordAccess.getU16(header, 4); - subtype = RecordAccess.getU16(header, 6); - recordlen = (int) (0xffffffff & RecordAccess.getU32(header, 8)); - - if (Debug.compileDebug) Debug.println("TIME: " + time + "\n TYPE: " + type - + "\n SUBTYPE: " + subtype + "\n RECORDLENGTH: " - + recordlen); - - this.record = new byte[recordlen]; - - leidos = this.in.read(record, 0, record.length); - - if (leidos != this.record.length) { - this.eof = true; - throw new BGPFileReaderException("Truncated file: " + leidos - + " instead of " + this.record.length + " bytes",record); - } - - /* - * Record parsing - */ - switch (type) { - case MRTConstants.TABLE_DUMP: - return parseTableDump(subtype); - - case MRTConstants.TABLE_DUMP_v2: - - switch (subtype) { - case MRTConstants.PEER_INDEX_TABLE: - parsePeerIndexTable(); - break; - case 2: - parseTableDumpv2(MRTConstants.AFI_IPv4); - break; - case 4: - parseTableDumpv2(MRTConstants.AFI_IPv6); - break; - case 6: - parseGenericRib(); - break; - case 3: - case 5: - parseTableDumpv2Multicast(); - break; - default: - throw new BGPFileReaderException( - "Unknown TABLE_DUMP_V2 subtype" + subtype, header); - } - break; - - case MRTConstants.BGP4MP: - if ((result = parseBgp4mp(subtype)) != null) - return result; - break; - - default: - result = new MRTRecord(); - result.setGeneric(header, record); - return result; - } - } - } - - private MRTRecord parseTableDump(int subtype) throws Exception { - switch (subtype) { - case MRTConstants.AFI_IPv4: - case MRTConstants.AFI_IPv6: - return new TableDump(header, record, subtype); - default: - throw new BGPFileReaderException("Unknown TABLE_DUMP subtype" - + subtype, header); - } - - } - - private MRTRecord parseBgp4mp(int subtype) throws Exception { - // System.out.println("parseBgp4mp("+MRTConstants.mpSubType(subtype)+")"); - switch (subtype) { - case MRTConstants.BGP4MP_MESSAGE: - case MRTConstants.BGP4MP_MESSAGE_AS4: - return parseBgp4Update((subtype == MRTConstants.BGP4MP_MESSAGE) ? 2 - : 4); - - /* - * TODO - * TTOODDOO:::: - * - * - * case BGP4MP_SNAPSHOT: return new Bgp4mpSnapshot(header,record); - */ - - case MRTConstants.BGP4MP_ENTRY: - return parseBgp4Entry(RecordAccess.getU16(record, 6)); - - case MRTConstants.BGP4MP_STATE_CHANGE: { - /* - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer AS number | Local AS number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Interface Index | Address Family | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer IP address (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Local IP address (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Old State | New State | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - int afi = RecordAccess.getU16(record, 6); - int addrOffs = 8; - int addrSize = (afi == MRTConstants.AFI_IPv4) ? 4 : 16; - int stateOffs = addrOffs + 2 * addrSize; - int oldState = RecordAccess.getU16(record, stateOffs); - stateOffs += 2; - int newState = RecordAccess.getU16(record, stateOffs); - - return new StateChange(RecordAccess.getU32(header, 0), InetAddress - .getByAddress(RecordAccess.getBytes(record, addrOffs, - addrSize)), new AS(RecordAccess.getU16(record, 0)), - oldState, newState); - - } - - case MRTConstants.BGP4MP_STATE_CHANGE_AS4: { - /* - * draft-ietf-grow-mrt-07.txt - * - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer AS number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Local AS number | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Interface Index | Address Family | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer IP address (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Local IP address (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Old State | New State | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - int afi = RecordAccess.getU16(record, 10); - int addrSize = (afi == MRTConstants.AFI_IPv4) ? 4 : 16; - int addrOffs = 12; - int stateOffs = addrOffs + 2 * addrSize; - int oldState = RecordAccess.getU16(record, stateOffs); - stateOffs += 2; - int newState = RecordAccess.getU16(record, stateOffs); - - return new StateChange(RecordAccess.getU32(header, 0), InetAddress - .getByAddress(RecordAccess.getBytes(record, addrOffs, - addrSize)), new AS(RecordAccess.getU32(record, 0)), - oldState, newState); - - } - - default: - break; - } - - MRTRecord result = new MRTRecord(); - result.setGeneric(header, record); - return result; - } - - private void parsePeerIndexTable() throws Exception { - /* - * System.out.println("in BGPFileReader.parsePeerIndexTable\nheader:"); - * RecordAccess.dump(header); System.out.println("record"); - * RecordAccess.dump(record); - */ - // System.err.println("\nin parsePeerIndexTable()..."); - /* - * 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | - * Collector BGP ID | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | - * View Name Length | View Name (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | - * Peer Count | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - int here = 0; - // long CollectorBGPId = RecordAccess.getU32 (this.record,here); - here += 4; - int ViewNameLen = RecordAccess.getU16(this.record, here); - here += 2; - // String ViewName = null; - if (ViewNameLen > 0) { - // TODO extract ViewName - here += ViewNameLen; - } - int PeerCount = RecordAccess.getU16(this.record, here); - here += 2; - - /* - * System.out.printf("Collector BGP Id: 0x%08X\n",CollectorBGPId); - * System.out.printf("View Name Length = %d\n",ViewNameLen); - * System.out.printf(" has %d peers\n",PeerCount); - */ - bgpId = new long[PeerCount]; - peerAS = new org.javamrt.mrt.AS[PeerCount]; - peerIP = new InetAddress[PeerCount]; - - for (int i = 0; i < PeerCount; i++) { - /* - * 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 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer Type | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer BGP ID | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer IP address (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer AS (variable) | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * The Peer Type field is a bit field which encodes the type of the - * AS and IP address as follows: - * - * Bit 0 - unset for IPv4 Peer IP address, set for IPv6 Bit 1 - - * unset when Peer AS field is 16 bits, set when it's 32 bits - */ - int peerType = RecordAccess.getU8(this.record, here++); - bgpId[i] = RecordAccess.getU32(this.record, here); - here += 4; - if ((peerType & 0x01) == 0) { - peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( - this.record, here, 4)); - here += 4; - } else { - peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( - this.record, here, 16)); - here += 16; - } - if ((peerType & 0x02) == 0) { - peerAS[i] = new AS(RecordAccess.getU16(this.record, here)); - here += 2; - } else { - peerAS[i] = new AS(RecordAccess.getU32(this.record, here)); - here += 4; - } - - // System.out.println("Peer "+i+"("+bgpId[i]+"): "+peerIP[i].getHostAddress()+" "+peerAS[i]); - } - - // System.exit(0); - } - - private long[] bgpId = null; - private org.javamrt.mrt.AS peerAS[] = null; - private java.net.InetAddress peerIP[] = null; - - private void parseTableDumpv2(int NLRItype) throws Exception { - - if (Debug.compileDebug) { - Debug.printf("parseTableDumpv2(%d)\nheader:", NLRItype); - Debug.dump(header); - Debug.println("record:"); - Debug.dump(record); - } - - int offset = 0; - long sequenceNo = RecordAccess.getU32(this.record, offset); - offset = 4; - Nlri nlri = new Nlri(this.record, offset, NLRItype); - offset += nlri.getOffset(); - - int entryCount = RecordAccess.getU16(this.record, offset); - offset += 2; - - if (debug) { - System.out.println("Sequence = " + sequenceNo); - System.out.println("NLRI = " + nlri.toPrefix().toString() - + " [" + nlri.getOffset() + "]"); - System.out.println("entries = " + entryCount); - } - - for (int i = 0; i < entryCount; i++) { - int peerIndex = RecordAccess.getU16(this.record, offset); - - if (debug) { - System.out.printf("peerIndex = %d; peer = %s(%s)\n", - peerIndex,MRTConstants.ipAddressString(peerIP[peerIndex]),peerAS[peerIndex].toString("AS")); - } - - offset += 2; - // TODO use origTime if appropriate - // - // long timeOrig=RecordAccess.getU32(this.record,offset); - offset += 4; - int attrLen = RecordAccess.getU16(this.record, offset); - offset += 2; - Attributes attributes = new Attributes(record, attrLen, offset,4); - offset += attrLen; - - - recordFifo.add(new TableDumpv2(1, // int view, - (int) (sequenceNo & 0xffff), nlri, time, peerIP[peerIndex], // InetAddress - // peer, - peerAS[peerIndex], attributes)); - } - } - - private MRTRecord parseBgp4Update(int asSize) throws Exception { - // Bgp4Update update; - - // TODO reconocer los AS de 4 bytes aquí - - int offset = 0; - AS srcAs = new AS(RecordAccess.getUINT(record, offset, asSize)); offset = asSize; - AS dstAs = new AS(RecordAccess.getUINT(record, offset, asSize)); offset +=asSize; - int iface = RecordAccess.getU16(record, offset); offset += 2; - int afi = RecordAccess.getU16(record, offset); offset += 2; -// int offset = 2 * asSize + 4; - int addrSize = (afi == MRTConstants.AFI_IPv4) ? 4 : 16; - - InetAddress srcIP = InetAddress.getByAddress(RecordAccess.getBytes( - record, offset, addrSize)); - offset += addrSize; - InetAddress dstIP = InetAddress.getByAddress(RecordAccess.getBytes( - record, offset, addrSize)); - offset += addrSize; - - /* - * skip the following 16 bytes which are the signature of the BGP header - */ - offset += 16; - - int bgpSize = RecordAccess.getU16(record, offset); offset += 2; - int bgpType = RecordAccess.getU8(record, offset); offset ++; - - if (Debug.compileDebug) { - Debug.printf("Bgp4Update(asSize = %d)\n", asSize); - Debug.printf("AS srcAs = %s\n", srcAs.toString()); - Debug.printf("AS dstAs = %s\n", dstAs.toString()); - Debug.printf("int iface = %d\n", iface); - Debug.printf("int Afi = %d\n", afi); - Debug.printf("int addrSize = %d\n", addrSize); - Debug.println("srcIP = " + srcIP.getHostAddress()); - Debug.println("dstIP = " + dstIP.getHostAddress()); - Debug.printf("bgpSize = %d\n", bgpSize); - Debug.println("bgpType = " + MRTConstants.bgpType(bgpType)); - Debug.dump(record); - } - switch (bgpType) { - case MRTConstants.BGP4MSG_KEEPALIVE: - return new KeepAlive(header, record); - - case MRTConstants.BGP4MSG_OPEN: - return new Open(header, record); - - case MRTConstants.BGP4MSG_NOTIFICATION: - return new Notification(header, record); - - case MRTConstants.BGP4MSG_UPDATE: - break; // to continue after case() - - case MRTConstants.BGP4MSG_REFRESH: - return new Refresh(header, record); - - default: - throw new Exception("Unknown BGP4 record type(" + bgpType + ")"); - } - - /* - * Here is where the update starts - */ - - int unfeasibleLen = RecordAccess.getU16(record, offset); - offset += 2; - if (Debug.compileDebug) Debug.printf("int unfeasibleLen = %d\n",unfeasibleLen); - - for (int i = 0; i < unfeasibleLen;) { - Nlri wNlri = new Nlri(record, offset, afi); - offset += wNlri.getOffset(); - i += wNlri.getOffset(); - - recordFifo - .add(new Withdraw(header, srcIP, srcAs, wNlri.toPrefix())); - } - - int attrLen = RecordAccess.getU16(record, offset); - if (Debug.compileDebug) Debug.printf("attrLen = %d, offset =%d (%d)\n",attrLen,offset,offset+attrLen+2); - offset += 2; - - if (attrLen > 0) { - Attributes attributes = null; - try { - attributes = new Attributes(record, attrLen, offset,asSize); - } catch (RFC4893Exception rfce) { - // - // piggyback peer and time info - // - rfce.setTimestamp(this.time); - rfce.setPeer(srcIP); - rfce.setAS(srcAs); - throw rfce; - } catch (Exception e) { - throw e; - } - // - // Process MP_REACH and MP_UNREACH - // - - MpUnReach mpUnreach = (MpUnReach) attributes - .getAttribute(MRTConstants.ATTRIBUTE_MP_UNREACH); - - if (mpUnreach != null) { - for (Nlri mpu : mpUnreach.getNlri()) { - recordFifo.add(new Withdraw(header, srcIP, srcAs, mpu - .toPrefix())); - } - } - - MpReach mpReach = (MpReach) attributes - .getAttribute(MRTConstants.ATTRIBUTE_MP_REACH); - - if (mpReach != null) { - if (Debug.compileDebug) Debug.printf("Has MP_REACH (%s)\n",mpReach.getNlri()); - for (Nlri mpu : mpReach.getNlri()) { - recordFifo.add(new Advertisement(header, srcIP, srcAs, mpu - .toPrefix(), attributes)); - } - } - - /* - * if (mpReach != null || mpUnreach != null) - * System.out.println("This is the whole record"); - * RecordAccess.dump(record); - * System.out.println("These are the attributes"); - * RecordAccess.dump(record,offset,attrLen); - * System.out.println("int attrLen = "+attrLen); - * - * throw new Exception("MP_REACH attribute!"); - */ - - offset += attrLen; - - if (Debug.compileDebug) Debug.debug("offset(%d) record.length (%d)\n",offset,record.length); - while (offset < record.length) { - Nlri aNlri = new Nlri(record, offset, afi); - offset += aNlri.getOffset(); - - recordFifo.add(new Advertisement(header, srcIP, srcAs, aNlri - .toPrefix(), attributes)); - } - } - if (recordFifo.isEmpty()) { - if (Debug.compileDebug) - if (Debug.doDebug) - throw new BGPFileReaderException("recordFifo empty!", record); - return null; - } - return recordFifo.remove(); - } - - private MRTRecord parseBgp4Entry(int AFI) throws Exception { - /* - * TODO: this doesn't work as expected yet - */ - if (Debug.compileDebug) { - Debug.debug("in parseBgp4Entry\n"); - Debug.dump(record); - } - int addrSize = (AFI == MRTConstants.AFI_IPv4) ? 4 : 16; - - int view = RecordAccess.getU16(record, 0); - int status = RecordAccess.getU16(record, 2); - long rtime = RecordAccess.getU32(record, 4); - int af = RecordAccess.getU16(record, 8); - int safi = RecordAccess.getU8(record, 10); - int nhl = RecordAccess.getU8(record, 11); - - if (Debug.compileDebug) { - Debug.debug("int view = %d\n",view); - Debug.debug("int status = %d\n",status); - Debug.debug("long rtime = %d\n",rtime); - Debug.debug("int af = %d\n",af); - Debug.debug("int safi = %d\n",safi); - Debug.debug("int nhl = %d\n",nhl); - } - int offset = 12; - InetAddress nextHop = InetAddress.getByAddress(RecordAccess.getBytes( - record, offset, addrSize)); - offset += addrSize; - Nlri prefix = new Nlri(record, offset, AFI); - offset += prefix.getOffset(); - - Attributes attrs = new Attributes(record, record.length - offset, - offset); - ASPath aspath = attrs.getASPath(); - - AS neighborAS = null; - - if (aspath != null) - neighborAS = aspath.get(0); - - return new TableDumpv2(view, 1, prefix, rtime, nextHop, neighborAS, - attrs); - } - - private void parseGenericRib() throws BGPFileReaderException{ - // TODO: implement - throw new BGPFileReaderException("TODO : parseGenericRib",new byte[1]); - } - - private void parseTableDumpv2Multicast() throws BGPFileReaderException { - // TODO: implement - throw new BGPFileReaderException("TODO: parseTableDumpv2Multicast()",new byte[1]); - } - - public boolean eof() { - return this.eof; - } - -} diff --git a/src/org/javamrt/mrt/KeepAlive.java b/src/org/javamrt/mrt/KeepAlive.java deleted file mode 100644 index deffafb..0000000 --- a/src/org/javamrt/mrt/KeepAlive.java +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -public class KeepAlive extends MRTRecord -{ - KeepAlive (byte[]header, byte[]record) - { - super (header); - } - - public String toString () - { - return "KEEP_ALIVE"; - } -} diff --git a/src/org/javamrt/mrt/Nlri.java b/src/org/javamrt/mrt/Nlri.java deleted file mode 100644 index 3416034..0000000 --- a/src/org/javamrt/mrt/Nlri.java +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -import org.javamrt.utils.RecordAccess; - -//IPv4/IPv6 address and mask length extractor from NLRI field in BGP - -public class Nlri - extends Prefix -{ - // from Prefix.java - // byte[] addr; - // int maskLength; - - public Nlri (byte[]record, int offset, int afi) - throws Exception - - { - super(); - /* - Receives whole BGP update package as byte [] record with integer offset as indicator of the position of length field - integer afi is used for deciding the type of address - masklen value is inside length octet, where the value is given in bits - +---------------------------+ - | Length (1 octet) | - +---------------------------+ - | Prefix (variable) | - +---------------------------+ - */ - if (afi==MRTConstants.AFI_IPv4) - this.base=new byte[4]; //deciding the type or address - else if (afi==MRTConstants.AFI_IPv6) - this.base=new byte[16]; - else - throw new Exception("NLRI: unknown Address Family: "+afi); - this.maskLength=RecordAccess.getU8(record, offset); //reading length byte (bits) - offset++; - this.bytes = 0; - if (this.maskLength > 0) - { - this.bytes=(this.maskLength-1)/8+1; //converting bits into bytes and deciding number of bytes to be read - } - - int i=0; - - while (i < bytes) - this.base[i++] = (byte)RecordAccess.getU8(record,offset++); //extracting byte by byte of prefix field - //and adding to address array - while (i < this.base.length) //filling up with zeros to complete the length of IPv4/v6 address - this.base[i++] = 0; - - setPrefix(this.base,this.maskLength); - } - - - public Prefix toPrefix() - { - return (Prefix)this; - } - - public int getOffset() //returning the record offset - { - return this.bytes+1; - } - - private int bytes; -} diff --git a/src/org/javamrt/mrt/Notification.java b/src/org/javamrt/mrt/Notification.java deleted file mode 100644 index 9fab6ea..0000000 --- a/src/org/javamrt/mrt/Notification.java +++ /dev/null @@ -1,21 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -public class Notification - extends MRTRecord -{ - public Notification (byte[]header, byte[]record) - { - super(header); - } - - public String toString () - { - return "NOTIFICATION"; - } -} diff --git a/src/org/javamrt/mrt/Open.java b/src/org/javamrt/mrt/Open.java deleted file mode 100644 index 1119cd1..0000000 --- a/src/org/javamrt/mrt/Open.java +++ /dev/null @@ -1,30 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.mrt; - -import org.javamrt.utils.Debug; - -public class Open - extends MRTRecord -{ - Open (byte[]header, byte[]record) - { - super (header); - - if (Debug.compileDebug) { - Debug.println("Open message"); - Debug.dump(header); - Debug.dump(record); - } - - } - - public String toString () - { - return "OPEN"; - } -} diff --git a/src/org/javamrt/progs/route_btoa.java b/src/org/javamrt/progs/route_btoa.java deleted file mode 100644 index 9cf86d5..0000000 --- a/src/org/javamrt/progs/route_btoa.java +++ /dev/null @@ -1,216 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.progs; - -import java.io.PrintStream; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import org.javamrt.mrt.AS; -import org.javamrt.mrt.BGPFileReader; -import org.javamrt.mrt.Bgp4Update; -import org.javamrt.mrt.KeepAlive; -import org.javamrt.mrt.MRTRecord; -import org.javamrt.mrt.Notification; -import org.javamrt.mrt.Open; -import org.javamrt.mrt.Prefix; -import org.javamrt.mrt.PrefixMaskException; -import org.javamrt.mrt.RFC4893Exception; -import org.javamrt.mrt.StateChange; -import org.javamrt.mrt.TableDump; -import org.javamrt.utils.Debug; -import org.javamrt.utils.getopts; - -public class route_btoa { - - private static Prefix prefix = null; - private static InetAddress peer = null; - private static AS originator = null; - private static AS traverses = null; - private static boolean showIPv4 = true; - private static boolean showIPv6 = true; - private static boolean printRFC4893violations = false; - - public static void main(String args[]) { - BGPFileReader in; - MRTRecord record; - getopts prueba; - - if (Debug.compileDebug) - prueba = new getopts(args, "46DhmP:p:o:t:v"); - else - prueba = new getopts(args, "46hmP:p:o:v:t:"); - - char opcion; - - boolean oldall = false; - - while ((opcion = prueba.nextOption()) != 0) { - try { - switch (opcion) { - case '4': - if (showIPv4 == true && showIPv6 == true) - showIPv6 = false; - else - System.exit(usage(1)); - break; - - case '6': - if (showIPv4 == true && showIPv6 == true) - showIPv4 = false; - else - System.exit(usage(1)); - break; - case 'D': - Debug.setDebug(true); - break; - case 'm': - oldall = true; - break; - case 'p': - peer = InetAddress.getByName(prueba.optarg); - break; - - case 'P': - prefix = Prefix.parseString(prueba.optarg); - break; - - case 'v': - printRFC4893violations = true; - break; - - case 'o': - originator = AS.parseString(prueba.optarg); - break; - case 't': - traverses = AS.parseString(prueba.optarg); - break; - case 'h': - default: - System.exit(usage((opcion) == 'h' ? 0 : 1)); - break; - } - } catch (UnknownHostException e) { - e.printStackTrace(); - System.exit(1); - } catch (PrefixMaskException e) { - e.printStackTrace(); - System.exit(1); - } catch (Exception e) { - e.printStackTrace(); - System.exit(1); - } - } - - if (args.length == prueba.optind) - System.exit(usage(0)); - - for (int arg = prueba.optind; arg < args.length; arg++) { - try { - in = new BGPFileReader(args[arg]); - while (false == in.eof()) { - try { - if ((record = in.readNext()) == null) - break; - if (record instanceof Open - || record instanceof KeepAlive - || record instanceof Notification) - continue; - if (record instanceof StateChange) { - if (oldall == true && checkPeer(record)) { - System.out.println(record.toString()); - continue; - } - } - if ((record instanceof TableDump) - || (record instanceof Bgp4Update)) { - - if (!showIPv4 && record.getPrefix().isIPv4()) - continue; - if (!showIPv6 && record.getPrefix().isIPv6()) - continue; - try { - if (!checkPrefix(record)) - continue; - if (!checkPeer(record)) - continue; - if (!checkASPath(record)) - continue; - } catch (Exception e) { - e.printStackTrace(System.err); - System.err.printf("record = %s\n",record); - } - System.out.println(record); - } - } catch (RFC4893Exception rfce) { - if (printRFC4893violations) - System.err.println(rfce.toString()); - } catch (Exception e) { - e.printStackTrace(System.err); - } - } - in.close(); - } catch (java.io.FileNotFoundException e) { - System.out.println("File not found: " + args[arg]); - } catch (Exception ex) { - System.err - .println("Exception caught when reading " + args[arg]); - ex.printStackTrace(System.err); - } - } // for (int arg... - } // main() - - - private static int usage(int retval) { - PrintStream ps = System.err; - - ps.println("route_btoa f1 ..."); - ps.println(" -h print this help message"); - ps.println(" -m legacy compatibility wth MRT: include all records"); - ps.println(" -4 print IPv4 prefixes only"); - ps.println(" -6 print IPv6 prefixes only"); - ps.println(" -p peer print updates from a specific peer only"); - ps.println(" -P prefix print updates for a specific prefix only"); - if (Debug.compileDebug) - ps.println(" -D enable debugging"); - ps.println(" -o as print updates generated by one AS only"); - ps.println(" -t as print updates where AS is in ASPATH"); - ps.println(" -4 and -6 together are not allowed"); - ps.println(" f1 ... are filenames or URL's"); - ps.println(" Use URL's according to the server's policies"); - ps.println(" Only prints records in machine readable format\n"); - - return retval; - } - - private static boolean checkPrefix(MRTRecord mrt) { - if (prefix == null) - return true; - return prefix.equals(mrt.getPrefix()); - } - - private static boolean checkPeer(MRTRecord mrt) { - if (peer == null) - return true; - return peer.equals(mrt.getPeer()); - } - - private static boolean checkASPath(MRTRecord mrt) { - if (originator == null) { - if (traverses == null) - return true; - // - // check whether AS is traversed by the prefix - // - return mrt.getASPath().contains(traverses); - } - // - // check whether the AS originates the prefix - // - return originator.equals(mrt.getASPath().generator()); - } -} diff --git a/src/org/javamrt/utils/RecordAccess.java b/src/org/javamrt/utils/RecordAccess.java deleted file mode 100644 index 1eafa22..0000000 --- a/src/org/javamrt/utils/RecordAccess.java +++ /dev/null @@ -1,114 +0,0 @@ -// This file is part of java-mrt -// A library to parse MRT files - -// This file is released under LGPL 3.0 -// http://www.gnu.org/licenses/lgpl-3.0-standalone.html - -package org.javamrt.utils; - -public class RecordAccess -{ - - static public long getU32 (byte[] buffer,int offset) - { - return getUINT(buffer, offset, 4); - } - - static public long getUINT (byte[]buffer, int offset,int size) - { - long result = 0; - // try { - for (int i = 0; i < size; i++) - { - result = ((result << 8) & 0xffffff00) + (buffer[offset + i] & 0xff); - } - /* - } catch (java.lang.ArrayIndexOutOfBoundsException aioobe) { - aioobe.printStackTrace(System.err); - System.err.printf("Accessing %d bytes long buffer at pos %d\n",buffer.length,offset); - dump(System.err,buffer); - System.exit(1); - } - */ - return result; - } - - static public int getU16 (byte[]buffer, int offset) - { - int result = 0; - for (int i = 0; i < 2; i++) - { - result = ((result << 8) & 0xff00) + (buffer[offset + i] & 0xff); - } - return result; - } - - static public int getU8 (byte[]buffer, int offset) - { - return ((int) buffer[offset] & 0x000000ff); - } - - static public byte[] getBytes (byte[]buffer, int offset, int length) - { - byte[]result = new byte[length]; - for (int i = 0; i < length; i++) - result[i] = buffer[offset + i]; - return result; - } - - static public String arrayToString(byte[] buffer) - { - return arrayToString(buffer,0,buffer.length); - } - - static public String arrayToString(byte[] buffer,int offset,int len) - { - StringBuffer result = new StringBuffer(); - - - for (int i=offset - (offset % 8);i < offset+len;i++) - { - if (i % 8 == 0) - result.append(String.format("\n%4d\t",i)); - if (i >= offset) - result.append(String.format("%02X ",buffer[i])); - else - result.append(" "); - } - result.append("\n"); - - return result.toString(); - } - - static public void dump(java.io.PrintStream out,byte[] buffer,int offset,int len) - { - out.print(arrayToString(buffer,offset,len)); - } - - static public void dump(byte[] buffer,int offset,int len) - { - dump(System.out,buffer,offset,len); - } - - static public void dump(java.io.PrintStream out,byte[] buffer,int len) - { - out.print(arrayToString(buffer,0,len)); - } - - static public void dump(byte[] buffer,int len) - { - dump(System.out,buffer,len); - } - - static public void dump(java.io.PrintStream out,byte[] buffer) - { - out.printf("buffer is %d bytes long",buffer.length); - out.print(arrayToString(buffer,0,buffer.length)); - } - - static public void dump(byte[] buffer) - { - dump(System.out,buffer,buffer.length); - } - -} diff --git a/src/test/java/org/javamrt/mrt/ASPathTest.java b/src/test/java/org/javamrt/mrt/ASPathTest.java new file mode 100644 index 0000000..b548756 --- /dev/null +++ b/src/test/java/org/javamrt/mrt/ASPathTest.java @@ -0,0 +1,135 @@ +package org.javamrt.mrt; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.collections.Lists; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class ASPathTest { + + + // BGP4MP|1519047444|A|2001:478:124::146|25152|2405:6e00::/32|25152 6939 2299530082 133612 {18291} {18291}|IGP|2001:478:124::176|0|0||NAG|65512 10.247.255.147| + private String as4update = + "WorTFAAQAAQAAACQAABiQAAAMW4AAAACIAEEeAEkAAAAAAAAAAABRiABBHgBJAAAAAAAAAAAAXH/////////////////////AGQCAAAATYAOGgACARAgAQR4ASQAAAAAAAAAAAF2ACAkBW4AQAEBAEACHgIEAABiQAAAGxuJEAtiAAIJ7AEBAABHcwEBAABHc8AHCAAA/+gK9/+T"; + + // BGP4MP|1517836325|A|195.66.224.133|22822|45.127.112.0/23|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|103.196.39.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|103.196.38.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|103.196.36.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|103.196.37.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|45.127.114.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + // BGP4MP|1517836325|A|195.66.224.133|22822|45.127.115.0/24|22822 54825 40138|IGP|195.66.224.133|0|0||NAG|| + private String as2Update = + "WnhYJQAQAAEAAABgWSYxbgAAAAHDQuCFw0Lh8f////////////////////8AUAIAAAAdQAEBAEACCAIDWSbWKZzKQAMEw0LghYAEBAAAAAAXLX9wGGfEJxhnxCYYZ8QkGGfEJRgtf3IYLX9z"; + + + @Test + public void testShouldCreateAsPathFromAs4Update() { + List mrtRecords = parseMrts(as4update); + Assert.assertEquals(mrtRecords.size(), 1); + MRTRecord mrtRecord = mrtRecords.get(0); + Assert.assertTrue(mrtRecord instanceof Advertisement); + ASPath asPath = mrtRecord.getASPath(); + Assert.assertEquals(asPath.toString(), "25152 6939 2299530082 133612 18291 18291"); + } + + @Test + public void testShouldCreateAsPathFromAs2Update() { + List mrtRecords = parseMrts(as2Update); + Assert.assertEquals(mrtRecords.size(), 7); + for (final MRTRecord mrtRecord : mrtRecords) { + Assert.assertTrue(mrtRecord instanceof Advertisement); + ASPath asPath = mrtRecord.getASPath(); + Assert.assertEquals(asPath.toString(), "22822 54825 40138"); + } + } + + @Test + public void testParseAsPathFromString() { + testParseAsPath(""); + testParseAsPath("1", new AS(1)); + testParseAsPath("1 1 1", new AS(1), new AS(1), new AS(1)); + testParseAsPath("22822 22822 22822", new AS(22822), new AS(22822), new AS(22822)); + testParseAsPath("1 2 3", new AS(1), new AS(2), new AS(3)); + testParseAsPath("22822 54825 40138", new AS(22822), new AS(54825), new AS(40138)); + testParseAsPath("(1 2 3)", new ASSet(Arrays.asList(new AS(1), new AS(2), new AS(3)))); + testParseAsPath("(1 2) 3", new ASSet(Arrays.asList(new AS(1), new AS(2))), new AS(3)); + testParseAsPath("1 (2 3)", new AS(1), new ASSet(Arrays.asList(new AS(2), new AS(3)))); + testParseAsPath("(22822 54825 40138)", new ASSet(Arrays.asList(new AS(22822), new AS(54825), new AS(40138)))); + testParseAsPath("(22822 54825) 40138", new ASSet(Arrays.asList(new AS(22822), new AS(54825))), new AS(40138)); + testParseAsPath("22822 (54825 40138)", new AS(22822), new ASSet(Arrays.asList(new AS(54825), new AS(40138)))); + testParseAsPath("1 (2 3) [4 5]", + new AS(1), + new ASSet(Arrays.asList(new AS(2), new AS(3))), + new ASConfedSequence(new LinkedList<>(Arrays.asList(new AS(4), new AS(5))))); + testParseAsPath("1 (2 3) {4 5}", + new AS(1), + new ASSet(Arrays.asList(new AS(2), new AS(3))), + new ASConfedSet(new LinkedList<>(Arrays.asList(new AS(4), new AS(5))))); + } + + @Test + public void testOriginating() { + Assert.assertNull(new ASPath(Collections.emptyList()).getOriginating()); + final AS as1 = new AS(1); + final AS as2 = new AS(2); + final AS as3 = new AS(3); + Assert.assertEquals(new ASPath(Collections.singletonList(as1)).getOriginating(), as1); + Assert.assertEquals(new ASPath(Arrays.asList(as1, as2, as3)).getOriginating(), as3); + Assert.assertEquals(new ASPath(Arrays.asList(as1, as1, as1)).getOriginating(), as1); + Assert.assertEquals(new ASPath(Arrays.asList(new ASSet(Arrays.asList(as1, as2)), as3)).getOriginating(), as3); + Assert.assertEquals(new ASPath(Arrays.asList(as1, new ASSet(Arrays.asList(as2, as3)))).getOriginating(), + new ASSet(Arrays.asList(as2, as3))); + } + + @Test + public void testTransiting() { + Assert.assertEquals(new ASPath(Collections.emptyList()).getTransiting(), Collections.emptyList()); + final AS as1 = new AS(1); + final AS as2 = new AS(2); + final AS as3 = new AS(3); + Assert.assertEquals(new ASPath(Collections.singletonList(as1)).getTransiting(), Collections.emptyList()); + Assert.assertEquals(new ASPath(Arrays.asList(as1, as2, as3)).getTransiting(), Arrays.asList(as1, as2)); + Assert.assertEquals(new ASPath(Arrays.asList(as1, as1, as1)).getTransiting(), Arrays.asList(as1, as1)); + Assert.assertEquals(new ASPath(Arrays.asList(new ASSet(Arrays.asList(as1, as2)), as3)).getTransiting(), + Collections.singletonList(new ASSet(Arrays.asList(as1, as2)))); + Assert.assertEquals(new ASPath(Arrays.asList(as1, new ASSet(Arrays.asList(as2, as3)))).getTransiting(), + Collections.singletonList(as1)); + } + + private static void testParseAsPath(final String input, final AS... expected) { + Assert.assertEquals(ASPath.fromString(input).getPath(), Lists.newArrayList(expected)); + } + + public static List parseMrts(String base64) { + byte[] bytes = Base64.getDecoder().decode(base64); + return parseMrts(bytes); + } + + public static List parseMrts(byte[] bytes) { + List result = new ArrayList<>(); + + try (BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(bytes))) { + BGPFileReader reader = new BGPFileReader(in); + while (!reader.eof()) { + MRTRecord next = reader.readNext(); + if (next != null) { + result.add(next); + } + } + } catch (Exception e) { + final String encoded = Base64.getEncoder().encodeToString(bytes); + throw new RuntimeException("Error parsing MRT bytes " + encoded, e); + } + + return result; + } +} diff --git a/src/test/java/org/javamrt/mrt/ASTest.java b/src/test/java/org/javamrt/mrt/ASTest.java new file mode 100644 index 0000000..cc94dfd --- /dev/null +++ b/src/test/java/org/javamrt/mrt/ASTest.java @@ -0,0 +1,95 @@ +package org.javamrt.mrt; + +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.testng.AssertJUnit.assertEquals; + +public class ASTest { + + @Test + public void should_create_AS() { + assertEquals(0L, new AS(0L).getASN()); + assertEquals(5L, new AS(5L).getASN()); + assertEquals(65535L, new AS(65535L).getASN()); + assertEquals(65536L, new AS(65536L).getASN()); + assertEquals(0xFFFFFFFFL, new AS(0xFFFFFFFFL).getASN()); + } + + @Test + public void should_create_AS_from_2_bytes() { + assertEquals(0L, new AS(new byte[]{0,0}).getASN()); + assertEquals(5L, new AS(new byte[]{0,5}).getASN()); + assertEquals(255L, new AS(new byte[]{0,-1}).getASN()); + assertEquals(256L, new AS(new byte[]{1,0}).getASN()); + assertEquals(0xffffL, new AS(new byte[]{-1,-1}).getASN()); + assertEquals(0x8990L, new AS(new byte[]{(byte)0x89,(byte)0x90}).getASN()); + } + + @Test + public void should_create_AS_from_4_bytes() { + assertEquals(0L, new AS(new byte[]{0,0,0,0}).getASN()); + assertEquals(5L, new AS(new byte[]{0,0,0,5}).getASN()); + assertEquals(65535L, new AS(new byte[]{0,0,-1,-1}).getASN()); + assertEquals(65536L, new AS(new byte[]{0,1,0,0}).getASN()); + assertEquals(0xFFFFFFFFL, new AS(new byte[]{-1,-1,-1,-1}).getASN()); + } + + @Test + public void should_print_AS() { + assertEquals("5", new AS(5L).toString()); + assertEquals("0", new AS(0L).toString()); + assertEquals("65535", new AS(65535L).toString()); + assertEquals("65536", new AS(65536L).toString()); + assertEquals("4294967295", new AS(0xFFFFFFFFL).toString()); + } + + @Test + public void should_compare() { + assertEquals(0, new AS(0L).compareTo(new AS(0L))); + assertEquals(0, new AS(5L).compareTo(new AS(5L))); + assertEquals(0, new AS(65535L).compareTo(new AS(65535L))); + assertEquals(0, new AS(65536L).compareTo(new AS(65536L))); + assertEquals(0, new AS(0xFFFFFFFFL).compareTo(new AS(0xFFFFFFFFL))); + + assertEquals(1, new AS(5L).compareTo(new AS(0L))); + assertEquals(1, new AS(65535L).compareTo(new AS(5L))); + assertEquals(1, new AS(65536L).compareTo(new AS(65535L))); + assertEquals(1, new AS(0xFFFFFFFFL).compareTo(new AS(65536L))); + + assertEquals(-1, new AS(0L).compareTo(new AS(5L))); + assertEquals(-1, new AS(5L).compareTo(new AS(65535L))); + assertEquals(-1, new AS(65535L).compareTo(new AS(65536L))); + assertEquals(-1, new AS(65536L).compareTo(new AS(0xFFFFFFFFL))); + } + + @Test + public void should_equal() { + assertEquals(new AS(0L), new AS(0L)); + assertEquals(new AS(5L), new AS(5L)); + assertEquals(new AS(65535L), new AS(65535L)); + assertEquals(new AS(65536L), new AS(65536L)); + assertEquals(new AS(0xFFFFFFFFL), new AS(0xFFFFFFFFL)); + } + + @Test + public void should_recognise_4_byte_asn() { + assertEquals(false, new AS(0L).is4Byte()); + assertEquals(false, new AS(5L).is4Byte()); + assertEquals(false, new AS(65535L).is4Byte()); + assertEquals(true, new AS(65536L).is4Byte()); + assertEquals(true, new AS(0xFFFFFFFFL).is4Byte()); + } + + @Test + public void getAsList() { + final AS as1 = new AS(1L); + final AS as2 = new AS(2L); + assertEquals(Collections.singletonList(as1), as1.getASList()); + assertEquals(Arrays.asList(as1, as2), new ASSet(Arrays.asList(as1, as2)).getASList()); + assertEquals(Arrays.asList(as1, as2), new ASConfedSet(Arrays.asList(as1, as2)).getASList()); + assertEquals(Arrays.asList(as1, as2), new ASConfedSequence(Arrays.asList(as1, as2)).getASList()); + } +} diff --git a/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java b/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java new file mode 100644 index 0000000..2f6f56d --- /dev/null +++ b/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java @@ -0,0 +1,60 @@ +package org.javamrt.mrt; + + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertNull; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class LargeCommunitiesTest { + @Test + public void testParseDump() + { + Exception exception = null; + List largeCommunities = + new ArrayList(); + + try (InputStream input = + getClass().getResourceAsStream("/updates.20170103.0630")) { + BGPFileReader bgpFileReader = + new BGPFileReader(new BufferedInputStream(input)); + while (true) { + MRTRecord mrtRecord = bgpFileReader.readNext(); + if (mrtRecord == null) { + break; + } + if (mrtRecord instanceof Bgp4Update) { + Bgp4Update bgp4update = (Bgp4Update) mrtRecord; + Attributes attributes = bgp4update.getAttributes(); + if (attributes != null) { + LargeCommunity largeCommunity = + attributes.getLargeCommunity(); + if (largeCommunity != null) { + largeCommunities.add(largeCommunity); + } + } + } + } + } catch (Exception e) { + exception = e; + } + assertNull(exception); + + assertEquals(largeCommunities.size(), 4); + + Set strings = + largeCommunities.stream() + .map(LargeCommunity::toString) + .collect(Collectors.toSet()); + assertEquals(strings.size(), 1); + assertEquals(strings.iterator().next(), "200753:200:46524131"); + } +} diff --git a/src/test/java/org/javamrt/mrt/MrtTest.java b/src/test/java/org/javamrt/mrt/MrtTest.java new file mode 100644 index 0000000..fc52a2e --- /dev/null +++ b/src/test/java/org/javamrt/mrt/MrtTest.java @@ -0,0 +1,191 @@ +package org.javamrt.mrt; + +import org.testng.annotations.Test; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +public class MrtTest { + + private Base64.Decoder b64 = Base64.getDecoder(); + + @Test + public void testParseUnsignedAsn() + { + try { + String asPath = "[37989, 4844, 7473, 6461, 209, 4200000013]"; + byte[] asnBuffer = new byte[]{2, 6, 0, 0, -108, 101, 0, 0, 18, -20, 0, 0, 29, 49, 0, 0, 25, 61, 0, 0, 0, -47, -6, 86, -22, 13}; + + int asSize = 4; + int offset = 0; + + ASPathSegment asPathSegment = new ASPathSegment(asnBuffer, offset, asSize); + assertEquals(asPathSegment.toString(), asPath); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + public void testMrtIpv6peerIpv4Prefix(){ + List result = new ArrayList<>(11); + byte[] header = new byte[] {90, 123, 73, 112, 0, 16, 0, 4, 0, 0, 0, -108}; + byte[] body = new byte[] {0, 0, 99, 91, 0, 0, 49, 110, 0, 0, 0, 2, 32, 1, 7, -8, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, -78, 32, 1, 7, -8, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 104, 2, 0, 0, 0, 37, 64, 1, 1, 0, 80, 2, 0, 22, 2, 5, 0, 0, 99, 91, 0, 0, 27, 27, 0, 0, 18, -38, 0, 0, -105, -109, 0, 0, -37, -117, 64, 3, 4, 62, 121, -64, 1, 22, 1, 0, 4, 22, 103, 29, 76, 24, 103, 2, -77, 24, 103, 2, -78, 24, 103, 2, -79, 24, 103, 2, -80, 22, 103, 2, -80, 24, 1, 0, 7, 24, 1, 0, 6, 24, 1, 0, 5, 24, 1, 0, 4}; + + byte[] mrt = new byte[] {90, 123, 73, 112, 0, 16, 0, 4, 0, 0, 0, -108,0, 0, 99, 91, 0, 0, 49, 110, 0, 0, 0, 2, 32, 1, 7, -8, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, -78, 32, 1, 7, -8, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 104, 2, 0, 0, 0, 37, 64, 1, 1, 0, 80, 2, 0, 22, 2, 5, 0, 0, 99, 91, 0, 0, 27, 27, 0, 0, 18, -38, 0, 0, -105, -109, 0, 0, -37, -117, 64, 3, 4, 62, 121, -64, 1, 22, 1, 0, 4, 22, 103, 29, 76, 24, 103, 2, -77, 24, 103, 2, -78, 24, 103, 2, -79, 24, 103, 2, -80, 22, 103, 2, -80, 24, 1, 0, 7, 24, 1, 0, 6, 24, 1, 0, 5, 24, 1, 0, 4}; + String[] resultStr = new String[] {"BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|1.0.4.0/22|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.29.76.0/22|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.2.179.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.2.178.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.2.177.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.2.176.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|103.2.176.0/22|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|1.0.7.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|1.0.6.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|1.0.5.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||", + "BGP4MP|1518029168|A|2001:7f8:24:0:0:0:0:b2|25435|1.0.4.0/24|25435 6939 4826 38803 56203|IGP|62.121.192.1|0|0||NAG||"}; + + try (BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(mrt))) { + BGPFileReader reader = new BGPFileReader(in); + +// MRTRecord next = reader.readNext(); + while (!reader.eof()) { + MRTRecord next = reader.readNext(); + if (next != null) { + result.add(next.toString()); + } + } + assertEquals(result.toArray(), resultStr); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test + public void testIpv4EmbeddedIpv6With1(){ + + byte[] prefixBuffer = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -94, 97, 44, 0}; + int netmask = 118; + + try { + Prefix prefix = new Prefix(prefixBuffer, netmask); + String prefixResult = "::ffff:162.97.44.0/118"; + assertEquals(prefix.toString(), prefixResult); + } catch (PrefixMaskException e) { + e.printStackTrace(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + + @Test + public void testIpv4EmbeddedIpv6With0(){ + + byte[] prefixBuffer = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 16, -88, 0}; + int netmask = 118; + + try { + Prefix prefix = new Prefix(prefixBuffer, netmask); + String prefixResult = "::67.16.168.0/118"; + assertEquals(prefix.toString(), prefixResult); + } catch (PrefixMaskException e) { + e.printStackTrace(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + + @Test + public void should_parse_empty_update() throws Exception { + final byte[] bytes = b64.decode("RtX+aQAQAAEAAAAnMr0xbgAAAAHDQuDjw0Lh8f////////////////////8AFwIAAAAA"); + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getPeer().toString(), "/195.66.224.227"); + assertEquals(mrtRecord.getPeerAS().toString(), "12989"); + assertEquals(mrtRecord.getTime(), 1188429417); + assertEquals(mrtRecord.getType(), MRTConstants.BGP4MP); + assertEquals(mrtRecord.getSubType(), MRTConstants.BGP4MP_MESSAGE); + assertNull(mrtRecord.getASPath()); + assertNull(mrtRecord.getPrefix()); + } + + @Test + public void should_parse_update_without_nlri() throws Exception { + final byte[] bytes = b64.decode("RwcWQgAQAAEAAAB+GxsxbgAAAAIgAQf4AAQAAAAAAAAbGwABIAEH+AAEAAAAAAAAMW4AAP////////////////////8AVgIAAAA/QAEBAEACEAIHGxsJ1B3sXt9fqlHlCx+ADiUAAgEgIAEH+AAEAAAAAAAAGxsAAf6AAAAAAAAAAgzb//7/EysA"); + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + final Bgp4Update mrtRecord = (Bgp4Update)bgpFileReader.readNext(); + assertEquals(mrtRecord.getPeer().toString(), "/2001:7f8:4:0:0:0:1b1b:1"); + assertEquals(mrtRecord.getPeerAS().toString(), "6939"); + assertEquals(mrtRecord.getTime(), 1191646786); + assertEquals(mrtRecord.getType(), MRTConstants.BGP4MP); + assertEquals(mrtRecord.getSubType(), MRTConstants.BGP4MP_MESSAGE); + assertEquals(mrtRecord.getASPath().toString(), "6939 2516 7660 24287 24490 20965 2847"); + assertNull(mrtRecord.getPrefix()); + assertEquals(mrtRecord.getAttributes().toString(), "6939 2516 7660 24287 24490 20965 2847|IGP|2001:7f8:4:0:0:0:1b1b:1|0|0||NAG||"); + } + + @Test + public void should_parse_8byte_state_change() throws Exception { + final byte[] bytes = b64.decode("OfZLTAAQAAAAAAAIAAAAAAADAAQ="); + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + BGPFileReader.setLenient(true); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getClass(), StateChange.class); + assertEquals(mrtRecord.toString(), "BGP4MP|972442444|STATE|0.0.0.0|0|3|4"); + } + + @Test + public void should_parse_AFI0_and_realign_bgp_message() throws Exception { + final byte[] bytes = b64.decode("OfZOxAAQAAEAAAAhAAAAAP////////////////////8AHQEEMGYAtNQyoccA"); + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + BGPFileReader.setLenient(true); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getClass(), Open.class); + assertEquals(mrtRecord.toString(), "OPEN|972443332|0.0.0.0|0|3560088007"); + } + + @Test + public void should_truncate_attributes_to_record_length() throws Exception { + final byte[] bytes = b64.decode("USa7OAAQAAQAAAByAAAjKgAAMW4AAAACIAEH+AAEAAAAAAAAIyoAASABB/gABAAAAAAAADFuAAD/////////////////////AEYCAAAAL0ABAQBAAgoCAgAAIyoAABp2kA4AGgACARAgAQf4AAQAAAAAAAAadgABACAgAQm4"); + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + BGPFileReader.setLenient(true); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getClass(), Advertisement.class); + assertEquals(mrtRecord.toString(), "BGP4MP|1361492792|A|2001:7f8:4:0:0:0:232a:1|9002|2001:9b8:0:0:0:0:0:0/32|9002 6774|IGP|2001:7f8:4:0:0:0:1a76:1|0|0||NAG||"); + } + + @Test + public void should_parse_ipv4_end_of_rib() throws Exception { + final byte[] bytes = new byte[] {91,15,-4,42,0,16,0,4,0,0,0,43,0,4,5,-96,0,0,49,110,0,0,0,1,-69,16,-36,-63,-69,16,-40,23,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,23,2,0,0,0,0}; + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getClass(), EndOfRib.class); + assertEquals(mrtRecord.toString(), "BGP4MP|1527774250||187.16.220.193|263584|"); + } + + @Test + public void should_parse_ipv6_end_of_rib() throws Exception { + final byte[] bytes = new byte[] {91,16,-4,-111,0,16,0,4,0,0,0,73,0,0,-77,72,0,0,49,110,0,0,0,2,32,1,13,-16,2,-24,16,0,0,0,0,0,0,0,0,1,32,1,6,124,2,-24,0,2,-1,-1,0,0,0,4,0,40,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,29,2,0,0,0,6,-128,15,3,0,2,1}; + final ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + final BGPFileReader bgpFileReader = new BGPFileReader(stream); + final MRTRecord mrtRecord = bgpFileReader.readNext(); + assertEquals(mrtRecord.getClass(), EndOfRib.class); + assertEquals(mrtRecord.toString(), "BGP4MP|1527839889||2001:df0:2e8:1000:0:0:0:1|45896|||255.255.255.255|0|0||NAG||"); + } +} diff --git a/src/test/resources/rrc20.updates.20180205.1030.gz b/src/test/resources/rrc20.updates.20180205.1030.gz new file mode 100644 index 0000000..1d33d92 Binary files /dev/null and b/src/test/resources/rrc20.updates.20180205.1030.gz differ diff --git a/src/test/resources/updates.20170103.0630 b/src/test/resources/updates.20170103.0630 new file mode 100644 index 0000000..606c906 Binary files /dev/null and b/src/test/resources/updates.20170103.0630 differ