diff --git a/src/main/java/org/javamrt/mrt/AS.java b/src/main/java/org/javamrt/mrt/AS.java index 3bb7e16..b834e74 100644 --- a/src/main/java/org/javamrt/mrt/AS.java +++ b/src/main/java/org/javamrt/mrt/AS.java @@ -7,7 +7,9 @@ package org.javamrt.mrt; import java.math.BigInteger; +import java.util.Collections; import java.util.Comparator; +import java.util.List; /** * @author paag @@ -146,4 +148,11 @@ public static AS parseString(String asspec) throws Exception { } 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 index 8492f17..5e36cb4 100644 --- a/src/main/java/org/javamrt/mrt/ASConfedSequence.java +++ b/src/main/java/org/javamrt/mrt/ASConfedSequence.java @@ -6,22 +6,25 @@ 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 class ASConfedSequence extends AS { + LinkedList asList; - public ASConfedSequence(LinkedList asList) { - this.asList = new LinkedList(); - this.asList.addAll(asList); - } + public ASConfedSequence(List 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(" ]"); - } + @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 index 082cd82..e0b1b1d 100644 --- a/src/main/java/org/javamrt/mrt/ASConfedSet.java +++ b/src/main/java/org/javamrt/mrt/ASConfedSet.java @@ -7,22 +7,26 @@ 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; + private LinkedList asList; - public ASConfedSet(LinkedList asList) { - this.asList = new LinkedList(); - this.asList.addAll(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(" ", "{", "}")); + } - public String toString() { - String result = "{"; - for (AS as:asList) - result = result.concat(" "+as.toString()); - return result.concat(" }"); - } + @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 index 0226c16..cdf02e1 100755 --- a/src/main/java/org/javamrt/mrt/ASPath.java +++ b/src/main/java/org/javamrt/mrt/ASPath.java @@ -6,315 +6,407 @@ 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 org.javamrt.utils.RecordAccess; +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
* - * @version 3.00 * @author paag + * @version 3.00 */ public class ASPath implements Attribute { - protected LinkedList path; - ASPath() { - this.path = new LinkedList(); - this.prependers = null; - } + 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(); - } - - /** - * @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; - } + 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/main/java/org/javamrt/mrt/ASSet.java b/src/main/java/org/javamrt/mrt/ASSet.java index cff53bc..459b268 100644 --- a/src/main/java/org/javamrt/mrt/ASSet.java +++ b/src/main/java/org/javamrt/mrt/ASSet.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; // import org.javamrt.utils.RecordAccess; @@ -39,6 +40,7 @@ public int hashCode() { return asSet.hashCode(); } + @Override public List getASList() { return this.asSet; } @@ -60,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/main/java/org/javamrt/mrt/Attributes.java b/src/main/java/org/javamrt/mrt/Attributes.java index 6d53ed1..c4a9acb 100644 --- a/src/main/java/org/javamrt/mrt/Attributes.java +++ b/src/main/java/org/javamrt/mrt/Attributes.java @@ -6,11 +6,11 @@ package org.javamrt.mrt; +import org.javamrt.progs.route_btoa; import org.javamrt.utils.Debug; import org.javamrt.utils.RecordAccess; import java.net.InetAddress; -import java.util.Arrays; import java.util.Vector; public class Attributes { @@ -22,25 +22,27 @@ public Attributes(byte[] record, int attrLen, int attrPos, int attrBytes) 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 = Arrays.copyOfRange(record, attrPos, attrPos + attrLen); + 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 = Arrays.copyOfRange(record, attrPos, attrPos + attrLen); + bytes = new byte[attrLen]; + System.arraycopy(record, attrPos, bytes, 0, attrLen); + decode(bytes, attrLen, 2); } public byte[] getBytes() { return bytes; } - private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) + 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); @@ -51,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); @@ -253,7 +255,7 @@ private void decode(byte[] record, int attrLen, int attrPos, int attrBytes) break; default: - attributes.set(type, new UnsupportedAttribute(type, buffer)); + route_btoa.System_err_println("Ignoring unknown attribute type " + type); break; } } @@ -272,7 +274,7 @@ 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) { diff --git a/src/main/java/org/javamrt/mrt/BGPFileReader.java b/src/main/java/org/javamrt/mrt/BGPFileReader.java index d8cbfad..d8e886c 100755 --- a/src/main/java/org/javamrt/mrt/BGPFileReader.java +++ b/src/main/java/org/javamrt/mrt/BGPFileReader.java @@ -10,23 +10,32 @@ import org.javamrt.utils.Debug; import org.javamrt.utils.RecordAccess; -import java.io.*; +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 { +public class BGPFileReader implements Closeable { private static final boolean debug = false; + private static boolean lenient = false; - private BufferedInputStream in = null; + private InputStream in; private LinkedList recordFifo; - private boolean eof = false; + 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 byte[] header; - private byte[] record; - - private String toString; + private String streamName; /***** * * public BGPFileReader (BufferedInputStream in) @@ -34,12 +43,10 @@ public class BGPFileReader { * create a new BGPFileReader from BufferedInputStream */ - public BGPFileReader(BufferedInputStream in) { + public BGPFileReader(InputStream in) { this.in = in; - this.toString = in.toString(); + this.streamName = in.toString(); this.recordFifo = new LinkedList(); - this.header = new byte[12]; // always 12 bytes, create once - this.record = null; this.eof = false; } @@ -49,12 +56,12 @@ public BGPFileReader(BufferedInputStream in) { * * create a new BGPFileReader from BufferedInputStream specified by the * String name - * @throws Exception + * @throws IOException */ - public BGPFileReader(String name) throws Exception { + public BGPFileReader(String name) throws IOException { InputStream inStream = null; - this.toString = name; + this.streamName = name; try { // to open the name as a URL java.net.URL url = new java.net.URL(name); @@ -67,31 +74,28 @@ public BGPFileReader(String name) throws Exception { } - if (this.toString.endsWith(".gz")) { - this.in = new BufferedInputStream(new GZIPInputStream(inStream)); + if (this.streamName.endsWith(".gz")) { + this.in = new GZIPInputStream(inStream, 8192); } else { - this.in = new BufferedInputStream(inStream); + this.in = (inStream instanceof BufferedInputStream || inStream instanceof GZIPInputStream) + ? inStream : 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(); + throw new java.io.FileNotFoundException(f.toString()); FileInputStream inStream = new FileInputStream(f); - this.toString = f.getCanonicalPath(); - if (this.toString.endsWith(".gz")) { - this.in = new BufferedInputStream(new GZIPInputStream(inStream)); + 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.header = new byte[12]; // always 12 bytes, create once - this.record = null; this.eof = false; } @@ -104,27 +108,15 @@ 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; + return this.streamName; } - /*** - * - * MRTRecord readNext() - * - * returns next record on successful completion null on EOF - * - * throws Exception when something goes wrong - */ - private int type = 0; - private int subtype = 0; private long time = 0; private long recordCounter = 0; @@ -155,17 +147,18 @@ public MRTRecord readNext() throws Exception { if (recordFifo.size() != 0) return recordFifo.remove(); - /* - * Help GC - */ - if (record != null) - record = null; /* * if the queue is empty, read from the file */ - int bytesRead = readFromInputStream(this.in, header, header.length); - recordCounter ++; + 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 */ @@ -176,16 +169,19 @@ record = null; /* * truncated */ - if (bytesRead != this.header.length) { + if (bytesRead != header.length) { this.eof = true; - throw new BGPFileReaderException("Truncated file: " + bytesRead - + " instead of " + this.header.length + " bytes", header); + 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); - type = RecordAccess.getU16(header, 4); - subtype = RecordAccess.getU16(header, 6); + final int type = RecordAccess.getU16(header, 4); + final int subtype = RecordAccess.getU16(header, 6); final long recordlen = RecordAccess.getU32(header, 8); @@ -193,37 +189,52 @@ record = null; + "\n SUBTYPE: " + subtype + "\n RECORDLENGTH: " + recordlen); - if (recordlen > Integer.MAX_VALUE) throw new RuntimeException("Can't have a record longer than 2147483647 bytes"); - this.record = new byte[(int) recordlen]; + if (recordlen > Integer.MAX_VALUE) { + throw new MalformedBgpStreamException("Can't have a record longer than 2147483647 bytes"); + } - bytesRead = readFromInputStream(this.in, record, record.length); + 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 != this.record.length) { - this.eof = true; - throw new BGPFileReaderException("Truncated file: " + bytesRead - + " instead of " + this.record.length + " bytes", header); + 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(this.record); + Debug.dump(record); /* * Record parsing */ switch (type) { case MRTConstants.TABLE_DUMP: - return parseTableDump(subtype); + return parseTableDump(header, record, subtype); case MRTConstants.TABLE_DUMP_v2: switch (subtype) { case MRTConstants.PEER_INDEX_TABLE: - parsePeerIndexTable(); + parsePeerIndexTable(record); break; case 2: - parseTableDumpv2(MRTConstants.AFI_IPv4); + parseTableDumpv2(header, record, MRTConstants.AFI_IPv4); break; case 4: - parseTableDumpv2(MRTConstants.AFI_IPv6); + parseTableDumpv2(header, record, MRTConstants.AFI_IPv6); break; case 6: parseGenericRib(); @@ -239,14 +250,14 @@ record = null; break; case MRTConstants.BGP4MP: - MRTRecord bgp4mp = parseBgp4mp(subtype); + MRTRecord bgp4mp = parseBgp4mp(header, record, subtype); if ((bgp4mp) != null) { return bgp4mp; } break; case MRTConstants.BGPDUMP_TYPE_MRTD_BGP: - MRTRecord bgpRecord = parseBgp(subtype); + MRTRecord bgpRecord = parseBgp(header, record, subtype); if ((bgpRecord) != null) { return bgpRecord; } @@ -261,7 +272,7 @@ record = null; * 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(BufferedInputStream bis, byte[] output, int length) throws IOException { + private static int readFromInputStream(InputStream bis, byte[] output, int length) throws IOException { int remaining = length; int read; @@ -270,7 +281,7 @@ private static int readFromInputStream(BufferedInputStream bis, byte[] output, i return ((read == -1 && remaining == length) ? -1 : length - remaining); } - private MRTRecord parseTableDump(int subtype) throws Exception { + private MRTRecord parseTableDump(byte[] header, byte[] record, int subtype) throws Exception { switch (subtype) { case MRTConstants.AFI_IPv4: case MRTConstants.AFI_IPv6: @@ -282,13 +293,15 @@ private MRTRecord parseTableDump(int subtype) throws Exception { } - private MRTRecord parseBgp(int subtype) throws Exception { + private MRTRecord parseBgp(byte[] header, byte[] record, int subtype) throws Exception { //Based on https://tools.ietf.org/html/rfc1771 - switch (subtype){ - case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_UPDATE: - return getBgpUpdate(); - case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_KEEPALIVE: - return new KeepAlive(header, record); + // 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; @@ -313,27 +326,42 @@ private MRTRecord parseBgp(int subtype) throws Exception { MRTConstants.UPDATE_STR_BGP, header, record); - default: - return new MRTRecord(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)); - private MRTRecord getBgpUpdate() throws Exception{ - int offset = 0; - int asSize = 2; - int addrSize = 4; + switch (subtype) { + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_UPDATE: + return getBgpUpdate(header, record, peerAS, peerIP); - AS srcAs = new AS(RecordAccess.getUINT(record, offset, asSize)); - offset = asSize; + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_OPEN: + final long bgpId = RecordAccess.getUINT(record, 12 + 5, 4); + return new Open(header, record, peerIP, peerAS, bgpId); - InetAddress srcIP = InetAddress.getByAddress(RecordAccess.getBytes(record, offset, addrSize)); - offset += addrSize; + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_NOTIFY: + return new Notification(header, record, peerIP, peerAS); - AS dstAs = new AS(RecordAccess.getUINT(record, offset, asSize)); - offset +=asSize; + case MRTConstants.BGPDUMP_SUBTYPE_MRTD_BGP_KEEPALIVE: + return new KeepAlive(header, record, peerAS, peerIP, localAS, localIP); + } + } + + return new MRTRecord(header, record); + } - InetAddress dstIP = InetAddress.getByAddress(RecordAccess.getBytes(record, offset, addrSize)); - offset += addrSize; + 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; @@ -341,60 +369,59 @@ private MRTRecord getBgpUpdate() throws Exception{ // WITHDRAWS if(wSize > 0) { // Withdraw if the size > than 0. for (int i = 0; i < wSize;) { - Nlri wNlri = new Nlri(record, offset, 1); - offset += wNlri.getOffset(); - i += wNlri.getOffset(); + 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, srcIP, srcAs, wNlri.toPrefix(),MRTConstants.UPDATE_STR_BGP); + Withdraw withdraw = new Withdraw(header, record, peerIP, peerAS, prefix, MRTConstants.UPDATE_STR_BGP); recordFifo.add(withdraw); } - }else{ // Advertisments - - //Reading length of attributes - int attrLen = RecordAccess.getU16(record, offset); - 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 - processReachAndUnreach(attributes, srcIP, srcAs, MRTConstants.UPDATE_STR_BGP); - - offset += attrLen; - while (offset < record.length) { - Nlri aNlri = new Nlri(record, offset, 1); - offset += aNlri.getOffset(); - - recordFifo.add(new Advertisement(header, record, srcIP, srcAs, - aNlri.toPrefix(), attributes, MRTConstants.UPDATE_STR_BGP)); - } - } } + // 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); - return recordFifo.remove(); + 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(int subtype) throws Exception { + 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 parseBgp4Update((subtype == MRTConstants.BGP4MP_MESSAGE) ? 2 - : 4); + return parseBgp4mpMessage(header, record, (subtype == MRTConstants.BGP4MP_MESSAGE) ? 2 : 4); /* * TODO @@ -405,25 +432,45 @@ private MRTRecord parseBgp4mp(int subtype) throws Exception { */ case MRTConstants.BGP4MP_ENTRY: - return parseBgp4Entry(RecordAccess.getU16(record, 6)); + 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 + * 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 | + * | Peer AS Number | Local AS Number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Interface Index | Address Family | + * | Interface Index | Address Family | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Peer IP address (variable) | + * | Peer IP Address (variable) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Local IP address (variable) | + * | Local IP Address (variable) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Old State | New State | + * | 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; @@ -492,7 +539,7 @@ private MRTRecord parseBgp4mp(int subtype) throws Exception { return new MRTRecord(header, record); } - private void parsePeerIndexTable() throws Exception { + 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"); @@ -512,14 +559,14 @@ private void parsePeerIndexTable() throws Exception { int here = 0; // long CollectorBGPId = RecordAccess.getU32 (this.record,here); here += 4; - int ViewNameLen = RecordAccess.getU16(this.record, here); + int ViewNameLen = RecordAccess.getU16(record, here); here += 2; // String ViewName = null; if (ViewNameLen > 0) { // TODO extract ViewName here += ViewNameLen; } - int PeerCount = RecordAccess.getU16(this.record, here); + int PeerCount = RecordAccess.getU16(record, here); here += 2; /* @@ -551,23 +598,23 @@ private void parsePeerIndexTable() throws Exception { * 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); + int peerType = RecordAccess.getU8(record, here++); + bgpId[i] = RecordAccess.getU32(record, here); here += 4; if ((peerType & 0x01) == 0) { peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( - this.record, here, 4)); + record, here, 4)); here += 4; } else { peerIP[i] = InetAddress.getByAddress(RecordAccess.getBytes( - this.record, here, 16)); + record, here, 16)); here += 16; } if ((peerType & 0x02) == 0) { - peerAS[i] = new AS(RecordAccess.getU16(this.record, here)); + peerAS[i] = new AS(RecordAccess.getU16(record, here)); here += 2; } else { - peerAS[i] = new AS(RecordAccess.getU32(this.record, here)); + peerAS[i] = new AS(RecordAccess.getU32(record, here)); here += 4; } @@ -581,33 +628,33 @@ private void parsePeerIndexTable() throws Exception { private org.javamrt.mrt.AS peerAS[] = null; private java.net.InetAddress peerIP[] = null; - private void parseTableDumpv2(int NLRItype) throws Exception { + private void parseTableDumpv2(byte[] header, byte[] record, int nlriType) throws Exception { if (Debug.compileDebug) { - Debug.printf("parseTableDumpv2(%d)\nheader:", NLRItype); + 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); + long sequenceNo = RecordAccess.getU32(record, offset); offset = 4; - Nlri nlri = new Nlri(this.record, offset, NLRItype); + Prefix nlri = lenient ? new LenientPrefix(record, offset, nlriType) : new Nlri(record, offset, nlriType); offset += nlri.getOffset(); - int entryCount = RecordAccess.getU16(this.record, offset); + int entryCount = RecordAccess.getU16(record, offset); offset += 2; if (debug) { route_btoa.System_err_println("Sequence = " + sequenceNo); - route_btoa.System_err_println("NLRI = " + nlri.toPrefix().toString() + 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(this.record, offset); + int peerIndex = RecordAccess.getU16(record, offset); if (debug) { route_btoa.System_err_println(String.format("peerIndex = %d; peer = %s(%s)\n", @@ -619,7 +666,7 @@ private void parseTableDumpv2(int NLRItype) throws Exception { // // long timeOrig=RecordAccess.getU32(this.record,offset); offset += 4; - int attrLen = RecordAccess.getU16(this.record, offset); + int attrLen = RecordAccess.getU16(record, offset); offset += 2; Attributes attributes = new Attributes(record, attrLen, offset,4); offset += attrLen; @@ -639,12 +686,12 @@ private void parseTableDumpv2(int NLRItype) throws Exception { } //Process MP_REACH and MP_UNREACH - private void processReachAndUnreach(Attributes attributes, InetAddress srcIP, AS srcAs, String updateStrType) throws Exception{ + 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.toPrefix(), updateStrType)); + recordFifo.add(new Withdraw(header, record, srcIP, srcAs, mpu, updateStrType)); } } @@ -653,37 +700,78 @@ private void processReachAndUnreach(Attributes attributes, InetAddress srcIP, AS 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, updateStrType)); + recordFifo.add(new Advertisement(header, record, srcIP, srcAs, mpu, + attributes, updateStrType)); } } } - 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; + 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 = InetAddress.getByAddress(RecordAccess.getBytes( - record, offset, addrSize)); - offset += addrSize; - InetAddress dstIP = InetAddress.getByAddress(RecordAccess.getBytes( - record, offset, addrSize)); - offset += addrSize; - + 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 */ - offset += 16; + int offset = buffer.position() + BGP_MARKER.length; int bgpSize = RecordAccess.getU16(record, offset); offset += 2; int bgpType = RecordAccess.getU8(record, offset); offset ++; @@ -703,7 +791,7 @@ private MRTRecord parseBgp4Update(int asSize) throws Exception { } switch (bgpType) { case MRTConstants.BGP4MSG_KEEPALIVE: - return new KeepAlive(header, record); + return new KeepAlive(header, record, srcAs, srcIP, dstAs, dstIP); case MRTConstants.BGP4MSG_OPEN: int offsetForOpen = offset+5; @@ -734,20 +822,27 @@ private MRTRecord parseBgp4Update(int asSize) throws Exception { for (int i = 0; i < unfeasibleLen;) { //The withdraws out of the Attributs are going to be always ipv4 - Nlri wNlri = new Nlri(record, offset, 1); + 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.toPrefix(),MRTConstants.UPDATE_STR_BGP4MP)); + .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) { - Attributes attributes = null; try { attributes = new Attributes(record, attrLen, offset,asSize); } catch (RFC4893Exception rfce) { @@ -758,12 +853,10 @@ private MRTRecord parseBgp4Update(int asSize) throws Exception { rfce.setPeer(srcIP); rfce.setAS(srcAs); throw rfce; - } catch (Exception e) { - throw e; } // Process MP_REACH and MP_UNREACH - processReachAndUnreach(attributes, srcIP, srcAs, MRTConstants.UPDATE_STR_BGP4MP); + processReachAndUnreach(header, record, attributes, srcIP, srcAs, MRTConstants.UPDATE_STR_BGP4MP); // MpUnReach mpUnreach = (MpUnReach) attributes @@ -802,23 +895,62 @@ private MRTRecord parseBgp4Update(int asSize) throws Exception { 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 - Nlri aNlri = new Nlri(record, offset, 1); + 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.toPrefix(), attributes, MRTConstants.UPDATE_STR_BGP4MP)); + aNlri, attributes, MRTConstants.UPDATE_STR_BGP4MP)); } } if (recordFifo.isEmpty()) { - if (Debug.compileDebug) - if (Debug.doDebug) - throw new BGPFileReaderException("recordFifo empty!", header); - return null; + return new EndOfRib(header, record, srcIP, srcAs, attributes, MRTConstants.UPDATE_STR_BGP4MP); + } else { + return recordFifo.remove(); } - return recordFifo.remove(); } - private MRTRecord parseBgp4Entry(int AFI) throws Exception { + 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 */ @@ -847,7 +979,7 @@ private MRTRecord parseBgp4Entry(int AFI) throws Exception { InetAddress nextHop = InetAddress.getByAddress(RecordAccess.getBytes( record, offset, addrSize)); offset += addrSize; - Nlri prefix = new Nlri(record, offset, AFI); + Prefix prefix = lenient ? new LenientPrefix(record, offset, AFI) : new Nlri(record, offset, AFI); offset += prefix.getOffset(); Attributes attrs = new Attributes(record, record.length - offset, @@ -877,4 +1009,16 @@ 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/main/java/org/javamrt/mrt/Bgp4Update.java b/src/main/java/org/javamrt/mrt/Bgp4Update.java index 2f232aa..f0105f7 100644 --- a/src/main/java/org/javamrt/mrt/Bgp4Update.java +++ b/src/main/java/org/javamrt/mrt/Bgp4Update.java @@ -8,6 +8,7 @@ import java.net.InetAddress; import java.util.Comparator; +import java.util.Objects; public class Bgp4Update extends MRTRecord @@ -49,7 +50,7 @@ public String toString() { .append(this.updateType).append('|') .append(peerString).append('|') .append(this.peerAS).append('|') - .append(this.prefix.toString()); + .append(this.prefix == null ? "" : this.prefix.toString()); if (this.updateAttr != null) result.append('|').append(this.updateAttr.toString()); @@ -70,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() { @@ -108,12 +109,12 @@ 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) { - result = Long.valueOf(this.getTime()).compareTo(other.getTime()); + result = Long.compare(this.getTime(), other.getTime()); /* * Ignore sorting by update type * diff --git a/src/main/java/org/javamrt/mrt/ClusterList.java b/src/main/java/org/javamrt/mrt/ClusterList.java index adedb24..ab1bc5f 100644 --- a/src/main/java/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/main/java/org/javamrt/mrt/Community.java b/src/main/java/org/javamrt/mrt/Community.java index f6f2c7c..d1744b6 100644 --- a/src/main/java/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/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/main/java/org/javamrt/mrt/KeepAlive.java b/src/main/java/org/javamrt/mrt/KeepAlive.java index 61de4dc..d41e484 100644 --- a/src/main/java/org/javamrt/mrt/KeepAlive.java +++ b/src/main/java/org/javamrt/mrt/KeepAlive.java @@ -6,15 +6,61 @@ package org.javamrt.mrt; -public class KeepAlive extends MRTRecord -{ - KeepAlive (byte[]header, byte[]record) - { - super (header, record); - } - - public String toString () - { - return "KEEP_ALIVE"; - } +import java.net.InetAddress; +import java.util.Objects; + +public class KeepAlive extends MRTRecord { + private final AS peerAs; + private final InetAddress peerIp; + private final AS localAs; + private final InetAddress localIp; + + KeepAlive(byte[] header, byte[] record, AS peerAs, InetAddress peerIp, AS localAs, InetAddress localIp) { + super(header, record); + this.peerAs = peerAs; + this.peerIp = peerIp; + this.localAs = localAs; + this.localIp = localIp; + } + + @Override + public InetAddress getPeer() { + return peerIp; + } + + @Override + public AS getPeerAS() { + return peerAs; + } + + public AS getLocalAs() { + return localAs; + } + + public InetAddress getLocalIp() { + return localIp; + } + + @Override + public String toString() { + return String.format("KEEP_ALIVE|%d|%s|%s", + getTime(), MRTConstants.ipAddressString(peerIp, false), peerAs); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final KeepAlive keepAlive = (KeepAlive) o; + return getTime() == keepAlive.getTime() && + Objects.equals(peerAs, keepAlive.peerAs) && + Objects.equals(peerIp, keepAlive.peerIp) && + Objects.equals(localAs, keepAlive.localAs) && + Objects.equals(localIp, keepAlive.localIp); + } + + @Override + public int hashCode() { + return Objects.hash(peerAs, peerIp, localAs, localIp); + } } diff --git a/src/main/java/org/javamrt/mrt/LenientPrefix.java b/src/main/java/org/javamrt/mrt/LenientPrefix.java new file mode 100644 index 0000000..2bc4b80 --- /dev/null +++ b/src/main/java/org/javamrt/mrt/LenientPrefix.java @@ -0,0 +1,36 @@ +package org.javamrt.mrt; + +import org.javamrt.progs.route_btoa; +import org.javamrt.utils.RecordAccess; + +import java.net.InetAddress; + +public class LenientPrefix extends Prefix { + + public LenientPrefix(byte[] record, int offset, int afi) { + super(); + maskLength = RecordAccess.getU8(record, offset++); + if (afi != MRTConstants.AFI_IPv4 && afi != MRTConstants.AFI_IPv6) { + route_btoa.System_err_println("Unknown AFI: " + afi + ". Assuming IPv4."); + } + base = new byte[afi == MRTConstants.AFI_IPv6 ? 16 : 4]; + mask = new byte[afi == MRTConstants.AFI_IPv6 ? 16 : 4]; + 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()); + 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/main/java/org/javamrt/mrt/LocalPref.java b/src/main/java/org/javamrt/mrt/LocalPref.java index bdaa7c1..bf2bcbb 100644 --- a/src/main/java/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/main/java/org/javamrt/mrt/MRTConstants.java b/src/main/java/org/javamrt/mrt/MRTConstants.java index 3135260..b557bf1 100644 --- a/src/main/java/org/javamrt/mrt/MRTConstants.java +++ b/src/main/java/org/javamrt/mrt/MRTConstants.java @@ -10,6 +10,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -122,14 +123,19 @@ public static String asPathString(int type) { public static final int BGP4MSG_REFRESH = 5; 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 final String mpSubType(int s) { + public static String mpSubType(int s) { switch (s) { case BGP4MP_STATE_CHANGE: return "BGP4MP_STATE_CHANGE"; @@ -148,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 "; @@ -167,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) { @@ -187,7 +193,7 @@ public static final String bgpType(int bgpType) { * * It looks if it is an ipv4 embedded in ipv6 */ - public static final boolean isInIpv4EmbeddedIpv6Format(InetAddress ia, int afi){ + public static boolean isInIpv4EmbeddedIpv6Format(InetAddress ia, int afi){ if(ia instanceof Inet4Address && afi == AFI_IPv6){ return true; } @@ -204,7 +210,7 @@ public static final boolean isInIpv4EmbeddedIpv6Format(InetAddress ia, int afi){ * * It looks if it is an ipv4 embedded in ipv6 */ - public static final boolean isInIpv4EmbeddedIpv6Format(byte[] base) { + public static boolean isInIpv4EmbeddedIpv6Format(byte[] base) { try { InetAddress ia = InetAddress.getByAddress(base); if (ia instanceof Inet4Address && base.length == IPv6_LENGTH) { @@ -213,8 +219,8 @@ public static final boolean isInIpv4EmbeddedIpv6Format(byte[] base) { if (ia instanceof Inet6Address && ((Inet6Address)ia).isIPv4CompatibleAddress()) { return true; } - }catch(Exception e) { - e.printStackTrace(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); } return false; } @@ -228,18 +234,12 @@ public static final boolean isInIpv4EmbeddedIpv6Format(byte[] base) { * * for all addresses, removes initial name */ - public static final String ipAddressString(InetAddress inetAddress, boolean isIpv4EmbeddedIpv6) { -// String ipAddressString = inetAddress.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){ @@ -252,19 +252,19 @@ else if (inetAddress instanceof Inet6Address){ if (ipAddressString.equals(":")) ipAddressString = "::"; - }catch(Exception e){ - e.printStackTrace(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); } return ipAddressString; } - public static final String ipAddressString(byte[] ipAddressBase, boolean isIpv4EmbeddedIpv6){ - try{ - return ipAddressString(InetAddress.getByAddress(ipAddressBase), isIpv4EmbeddedIpv6); - }catch(Exception e){ - e.printStackTrace(); - return new String("??/"); - } - } + 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/main/java/org/javamrt/mrt/MRTRecord.java b/src/main/java/org/javamrt/mrt/MRTRecord.java index 82c9874..fd0ff1d 100644 --- a/src/main/java/org/javamrt/mrt/MRTRecord.java +++ b/src/main/java/org/javamrt/mrt/MRTRecord.java @@ -13,8 +13,8 @@ public class MRTRecord { - protected byte[] header; - protected byte[] body; + final protected byte[] header; + final protected byte[] body; protected MRTRecord(byte[] header, byte[] body) { this.header = header; 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/main/java/org/javamrt/mrt/Nlri.java b/src/main/java/org/javamrt/mrt/Nlri.java index 3416034..e14315f 100644 --- a/src/main/java/org/javamrt/mrt/Nlri.java +++ b/src/main/java/org/javamrt/mrt/Nlri.java @@ -6,13 +6,13 @@ 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 -{ + extends Prefix { // from Prefix.java // byte[] addr; // int maskLength; @@ -32,41 +32,29 @@ public Nlri (byte[]record, int offset, int afi) | 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; + 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/OriginatorID.java b/src/main/java/org/javamrt/mrt/OriginatorID.java index 680461a..1c35943 100644 --- a/src/main/java/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/main/java/org/javamrt/mrt/Prefix.java b/src/main/java/org/javamrt/mrt/Prefix.java index a173687..ed25145 100644 --- a/src/main/java/org/javamrt/mrt/Prefix.java +++ b/src/main/java/org/javamrt/mrt/Prefix.java @@ -14,7 +14,7 @@ public class Prefix implements Comparable, Comparator { - private boolean isInIpv4EmbeddedIpv6Format = false; + protected boolean isInIpv4EmbeddedIpv6Format = false; // for Nlri.java protected byte[] base; @@ -25,14 +25,7 @@ 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, UnknownHostException { @@ -75,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); @@ -167,7 +157,7 @@ public String toString() { return MRTConstants.ipAddressString(this.base, isInIpv4EmbeddedIpv6Format). concat("/" + this.maskLength); } catch (Exception e) { - return new String("??/"+this.maskLength); + return "??/" + this.maskLength; } } @@ -219,4 +209,18 @@ public boolean isDefault() { return false; return true; } + + /** + * @return number of bytes required to represent this prefix + */ + public int nrBytes() { + return maskLength > 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/main/java/org/javamrt/mrt/PrefixMaskException.java b/src/main/java/org/javamrt/mrt/PrefixMaskException.java index 6fd52ee..e3e91d6 100644 --- a/src/main/java/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/main/java/org/javamrt/mrt/RFC4893.java b/src/main/java/org/javamrt/mrt/RFC4893.java index a1364f3..5a2d338 100755 --- a/src/main/java/org/javamrt/mrt/RFC4893.java +++ b/src/main/java/org/javamrt/mrt/RFC4893.java @@ -48,9 +48,19 @@ 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) { diff --git a/src/main/java/org/javamrt/mrt/RFC4893Exception.java b/src/main/java/org/javamrt/mrt/RFC4893Exception.java index 621c657..1709057 100644 --- a/src/main/java/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/main/java/org/javamrt/progs/route_btoa.java b/src/main/java/org/javamrt/progs/route_btoa.java index 8febd83..e9dc1f0 100755 --- a/src/main/java/org/javamrt/progs/route_btoa.java +++ b/src/main/java/org/javamrt/progs/route_btoa.java @@ -242,7 +242,9 @@ 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(); } } diff --git a/src/main/java/org/javamrt/utils/RecordAccess.java b/src/main/java/org/javamrt/utils/RecordAccess.java index e848187..d77a4dd 100755 --- a/src/main/java/org/javamrt/utils/RecordAccess.java +++ b/src/main/java/org/javamrt/utils/RecordAccess.java @@ -6,6 +6,10 @@ package org.javamrt.utils; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; + public class RecordAccess { @@ -14,33 +18,20 @@ 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) { - route_btoa.printStackTrace(aioobe); - route_btoa.System_err_println(String.format("Accessing %d bytes long buffer at pos %d",buffer.length,offset)); - dump(buffer); - route_btoa.exit(1); + 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 result; + return new BigInteger(1, Arrays.copyOfRange(buffer, offset, offset + size)).longValue(); } 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; + 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) @@ -50,20 +41,22 @@ static public int getU8 (byte[]buffer, int offset) 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; + 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) { - return arrayToString(buffer,0,buffer.length); + 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) { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); for (int i=offset - (offset % 8);i < offset+len;i++) @@ -111,4 +104,19 @@ 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/test/java/org/javamrt/mrt/ASPathTest.java b/src/test/java/org/javamrt/mrt/ASPathTest.java index cc3a413..b548756 100644 --- a/src/test/java/org/javamrt/mrt/ASPathTest.java +++ b/src/test/java/org/javamrt/mrt/ASPathTest.java @@ -2,15 +2,20 @@ 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"; @@ -47,6 +52,63 @@ public void testShouldCreateAsPathFromAs2Update() { } } + @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); @@ -70,5 +132,4 @@ public static List parseMrts(byte[] bytes) { return result; } - } diff --git a/src/test/java/org/javamrt/mrt/ASTest.java b/src/test/java/org/javamrt/mrt/ASTest.java index 9271ccd..cc94dfd 100644 --- a/src/test/java/org/javamrt/mrt/ASTest.java +++ b/src/test/java/org/javamrt/mrt/ASTest.java @@ -2,6 +2,9 @@ import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.Collections; + import static org.testng.AssertJUnit.assertEquals; public class ASTest { @@ -79,4 +82,14 @@ public void should_recognise_4_byte_asn() { 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/MrtTest.java b/src/test/java/org/javamrt/mrt/MrtTest.java index 442225c..fc52a2e 100644 --- a/src/test/java/org/javamrt/mrt/MrtTest.java +++ b/src/test/java/org/javamrt/mrt/MrtTest.java @@ -1,17 +1,21 @@ package org.javamrt.mrt; -import org.junit.Test; +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() { @@ -23,7 +27,7 @@ public void testParseUnsignedAsn() int offset = 0; ASPathSegment asPathSegment = new ASPathSegment(asnBuffer, offset, asSize); - assertEquals(asPath, asPathSegment.toString()); + assertEquals(asPathSegment.toString(), asPath); } catch (Exception e) { e.printStackTrace(); @@ -100,4 +104,88 @@ public void testIpv4EmbeddedIpv6With0(){ 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||"); + } }