diff --git a/pom.xml b/pom.xml
index f6c9438..dce40e3 100755
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
net.apnic.mrt
mrt
- 0.0.5
+ 0.0.6
jar
Java MRT File Dumper
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..9be360b 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);
@@ -247,13 +249,22 @@ private void decode(byte[] record, int attrLen, int attrPos, int attrBytes)
this.hasASPATHLimit = true;
break;
- case MRTConstants.LARGE_COMMUNITY:
- Attribute largeCommunity = new LargeCommunity(buffer);
- attributes.set(MRTConstants.ATTRIBUTE_LARGE_COMMUNITY, largeCommunity);
- break;
+ case MRTConstants.LARGE_COMMUNITY:
+ if (buffer.length > 0) {
+ Attribute largeCommunity = new LargeCommunity(buffer);
+ attributes.set(MRTConstants.ATTRIBUTE_LARGE_COMMUNITY, largeCommunity);
+ }
+ break;
default:
- attributes.set(type, new UnsupportedAttribute(type, buffer));
+ // make sure to not overwrite other attributes in the Vector,
+ // as index in the Vector is not the same as type value
+ if (type > MRTConstants.ATTRIBUTE_LARGE_COMMUNITY && type < MRTConstants.ATTRIBUTE_TOTAL) {
+ final UnsupportedAttribute attribute = new UnsupportedAttribute(type, buffer);
+ attributes.set(type, attribute);
+ } else {
+ route_btoa.System_err_println("Ignoring unknown attribute type " + type);
+ }
break;
}
}
@@ -272,7 +283,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..8bbecbc
--- /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|EOR|%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/mrt/UnsupportedAttribute.java b/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java
index 3b7cfc8..95d7030 100644
--- a/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java
+++ b/src/main/java/org/javamrt/mrt/UnsupportedAttribute.java
@@ -1,5 +1,7 @@
package org.javamrt.mrt;
+import org.javamrt.utils.RecordAccess;
+
import java.util.Arrays;
public class UnsupportedAttribute implements Attribute {
@@ -14,10 +16,7 @@ public UnsupportedAttribute(int type, byte[] buffer) {
@Override
public String toString() {
- return "UnsupportedAttribute{" +
- "type=" + type +
- ", buffer=" + Arrays.toString(buffer) +
- '}';
+ return "Attribute type " + type + ": " + RecordAccess.arrayToString(buffer);
}
@Override
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/LargeCommunitiesTest.java b/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java
index 2f6f56d..1221a9c 100644
--- a/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java
+++ b/src/test/java/org/javamrt/mrt/LargeCommunitiesTest.java
@@ -3,17 +3,18 @@
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.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Base64;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
public class LargeCommunitiesTest {
@Test
public void testParseDump()
@@ -57,4 +58,19 @@ public void testParseDump()
assertEquals(strings.size(), 1);
assertEquals(strings.iterator().next(), "200753:200:46524131");
}
+
+ @Test
+ public void should_parse_large_community_with_zero_length() throws Exception {
+ final String base64 = "X3+NoAAQAAQAAACRAADjuwAAMW4AAAABW840glvONP3/////////////////////AH0CADwYF4YQGBeGERgXlyEYF6mgGBerYBhAwAAYQMABGEDAAhhAwAMYQMAEGEDABRhybOsYcmz4GLYSyBi2EskAJkABAQBAAg4CAwAA47sAABsbAAB+BUADBFvONILACATjuwPo4CAAGJ73ew==";
+ final byte[] bytes = Base64.getDecoder().decode(base64);
+ final BGPFileReader bgpFileReader = new BGPFileReader(new ByteArrayInputStream(bytes));
+ MRTRecord mrtRecord = null;
+ MRTRecord tmp;
+ while (!bgpFileReader.eof()) {
+ tmp = bgpFileReader.readNext();
+ if (tmp != null) mrtRecord = tmp;
+ }
+ assertEquals(mrtRecord.getClass(), Advertisement.class);
+ assertEquals(mrtRecord.toString(), "BGP4MP|1602194848|A|91.206.52.130|58299|158.247.123.0/24|58299 6939 32261|IGP|91.206.52.130|0|0|58299:1000|NAG||");
+ }
}
diff --git a/src/test/java/org/javamrt/mrt/MrtTest.java b/src/test/java/org/javamrt/mrt/MrtTest.java
index 442225c..2158b91 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|EOR|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|EOR|2001:df0:2e8:1000:0:0:0:1|45896|||255.255.255.255|0|0||NAG||");
+ }
}