Skip to content

Commit

Permalink
Merge pull request #155 from RIPE-NCC/feature/roa-validation-on-inter…
Browse files Browse the repository at this point in the history
…face-types

Validation state logic should use interface types
  • Loading branch information
ties authored Apr 5, 2024
2 parents f25099c + af929ce commit a69e654
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 104 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ next (snapshot) release, e.g. `1.1-SNAPSHOT` after releasing `1.0`.
updates.
* Remove the dependency check maven plugin, which was unreliable, now that
dependabot does the dependency updates.
* Add and use interfaces for ValidationState logic

## 2024-02-29 1.38
* Raise RpkiCaCertificateRequestParserException instead of NPE when an
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
package net.ripe.rpki.commons.validation.roa;

import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.ripe.ipresource.Asn;
import net.ripe.ipresource.IpRange;
import net.ripe.rpki.commons.crypto.cms.roa.Roa;
import net.ripe.rpki.commons.crypto.cms.roa.RoaPrefix;
import net.ripe.rpki.commons.util.EqualsSupport;
import org.apache.commons.lang3.Validate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


/**
* A route allowed by a ROA configuration.
*/
public class AllowedRoute extends EqualsSupport implements Serializable {
private static final long serialVersionUID = 1L;
@ToString
@EqualsAndHashCode
public class AllowedRoute implements RoaPrefixData, Serializable {
private static final long serialVersionUID = 2L;

private final Asn asn;
private final IpRange prefix;
Expand All @@ -32,23 +35,24 @@ public AllowedRoute(Asn asn, IpRange prefix, int maximumLength) {
}

public static List<AllowedRoute> fromRoas(List<? extends Roa> roas) {
List<AllowedRoute> result = new ArrayList<AllowedRoute>();
for (Roa roa : roas) {
for (RoaPrefix roaPrefix : roa.getPrefixes()) {
result.add(new AllowedRoute(roa.getAsn(), roaPrefix.getPrefix(), roaPrefix.getEffectiveMaximumLength()));
}
}
return result;
return roas.stream()
.flatMap(roa ->
roa.getPrefixes().stream().map(roaPrefix -> new AllowedRoute(roa.getAsn(), roaPrefix.getPrefix(), roaPrefix.getEffectiveMaximumLength()))
)
.collect(Collectors.toList());
}

@Override
public Asn getAsn() {
return asn;
}

@Override
public IpRange getPrefix() {
return prefix;
}

@Override
public int getMaximumLength() {
return maximumLength;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
package net.ripe.rpki.commons.validation.roa;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import net.ripe.ipresource.Asn;
import net.ripe.ipresource.IpRange;
import org.apache.commons.lang3.Validate;

import java.io.Serializable;
import java.util.Comparator;

/**
* A route announced on BGP.
*/
public final class AnnouncedRoute implements Serializable {
@EqualsAndHashCode
@Getter(onMethod_ = {@Override})
public final class AnnouncedRoute implements Serializable, RouteData {
private static final long serialVersionUID = 1L;

public static final Comparator<AnnouncedRoute> ASN_PREFIX_COMPARATOR = new Comparator<AnnouncedRoute>() {
@Override
public int compare(AnnouncedRoute o1, AnnouncedRoute o2) {
int rc = o1.getOriginAsn().compareTo(o2.getOriginAsn());
if (rc != 0) {
return rc;
}
return o1.getPrefix().compareTo(o2.getPrefix());
}
};

private final Asn originAsn;
private final IpRange prefix;

Expand All @@ -33,38 +24,8 @@ public AnnouncedRoute(Asn originAsn, IpRange prefix) {
this.originAsn = originAsn;
this.prefix = prefix;
}

public Asn getOriginAsn() {
return originAsn;
}

public IpRange getPrefix() {
return prefix;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + originAsn.hashCode();
result = prime * result + prefix.hashCode();
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
AnnouncedRoute that = (AnnouncedRoute) obj;
return this.originAsn.equals(that.originAsn) && this.prefix.equals(that.prefix);
}

@Override
public String toString() {
return "AnnouncedRoute [originAsn=" + originAsn + ", prefix=" + prefix + "]";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.ripe.rpki.commons.validation.roa;

import net.ripe.ipresource.Asn;
import net.ripe.ipresource.IpRange;

import java.util.Comparator;

/**
* Represents a Validated Roa Prefix
*/
public interface RoaPrefixData extends Comparable<RoaPrefixData> {
Comparator<RoaPrefixData> ROA_PREFIX_DATA_COMPARATOR = Comparator.comparing(RoaPrefixData::getAsn).thenComparing(RoaPrefixData::getPrefix).thenComparing(RoaPrefixData::getMaximumLength);
Asn getAsn();

/**
* @return The prefix of the entity. <emph>MUST</emph> be a prefix and not a IP range
*/
IpRange getPrefix();

/**
* @return The maximum length of the VRP. The maximum length <emph>MUST</emph> be in the range (inclusive) between
* the prefix length and the length of addresses for the address family.
*/
int getMaximumLength();

@Override
default int compareTo(RoaPrefixData o) {
return ROA_PREFIX_DATA_COMPARATOR.compare(this, o);
}

default AllowedRoute toAllowedRoute() {
return new AllowedRoute(getAsn(), getPrefix(), getMaximumLength());
}
}
22 changes: 22 additions & 0 deletions src/main/java/net/ripe/rpki/commons/validation/roa/RouteData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.ripe.rpki.commons.validation.roa;

import net.ripe.ipresource.Asn;
import net.ripe.ipresource.IpRange;

import java.util.Comparator;

public interface RouteData extends Comparable<RouteData> {
Comparator<RouteData> ROUTE_DATA_COMPARATOR = Comparator.comparing(RouteData::getOriginAsn).thenComparing(RouteData::getPrefix);

Asn getOriginAsn();

/**
* @return The prefix of the entity. <emph>MUST</emph> be a prefix and not a IP range
*/
IpRange getPrefix();

@Override
default int compareTo(RouteData o) {
return ROUTE_DATA_COMPARATOR.compare(this, o);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package net.ripe.rpki.commons.validation.roa;

import lombok.Singular;
import lombok.experimental.UtilityClass;
import net.ripe.ipresource.IpRange;
import net.ripe.ipresource.IpResource;
import net.ripe.ipresource.etree.IpResourceIntervalStrategy;
import net.ripe.ipresource.etree.NestedIntervalMap;
import net.ripe.rpki.commons.crypto.cms.roa.RoaPrefix;

import java.util.LinkedList;
import java.util.List;
Expand All @@ -12,27 +15,26 @@
/**
* See http://tools.ietf.org/html/draft-ietf-sidr-roa-validation-10
*/
@UtilityClass
public class RouteOriginValidationPolicy {

public static NestedIntervalMap<IpResource, List<AllowedRoute>> allowedRoutesToNestedIntervalMap(Iterable<? extends AllowedRoute> allowedRoutes) {
NestedIntervalMap<IpResource, List<AllowedRoute>> result = new NestedIntervalMap<IpResource, List<AllowedRoute>>(IpResourceIntervalStrategy.getInstance());
for (AllowedRoute allowedRoute : allowedRoutes) {
List<AllowedRoute> allowed = result.findExact(allowedRoute.getPrefix());
public static <T extends RoaPrefixData> NestedIntervalMap<IpResource, List<T>> allowedRoutesToNestedIntervalMap(Iterable<T> allowedRoutes) {
NestedIntervalMap<IpResource, List<T>> result = new NestedIntervalMap<>(IpResourceIntervalStrategy.getInstance());
for (T allowedRoute : allowedRoutes) {
List<T> allowed = result.findExact(allowedRoute.getPrefix());
if (allowed == null) {
List<AllowedRoute> list = new LinkedList<AllowedRoute>();
list.add(allowedRoute);
result.put(allowedRoute.getPrefix(), list);
} else {
allowed.add(allowedRoute);
allowed = new LinkedList<>();
result.put(allowedRoute.getPrefix(), allowed);
}
allowed.add(allowedRoute);
}
return result;
}

public RouteValidityState validateAnnouncedRoute(NestedIntervalMap<IpResource, ? extends Iterable<? extends AllowedRoute>> allowedRoutes, AnnouncedRoute announcedRoute) {
public static <T extends RoaPrefixData, U extends RouteData> RouteValidityState validateAnnouncedRoute(NestedIntervalMap<IpResource, ? extends Iterable<T>> allowedRoutes, U announcedRoute) {
RouteValidityState result = RouteValidityState.UNKNOWN;
for (Iterable<? extends AllowedRoute> routes : allowedRoutes.findExactAndAllLessSpecific(announcedRoute.getPrefix())) {
for (AllowedRoute allowedRoute : routes) {
for (var routes : allowedRoutes.findExactAndAllLessSpecific(announcedRoute.getPrefix())) {
for (T allowedRoute : routes) {
switch (validate(allowedRoute, announcedRoute)) {
case VALID:
return RouteValidityState.VALID;
Expand All @@ -50,7 +52,7 @@ public RouteValidityState validateAnnouncedRoute(NestedIntervalMap<IpResource, ?
return result;
}

private RouteValidityState validate(AllowedRoute allowedRoute, AnnouncedRoute announcedRoute) {
private static <T extends RoaPrefixData, U extends RouteData> RouteValidityState validate(T allowedRoute, U announcedRoute) {
IpRange announcedPrefix = announcedRoute.getPrefix();

if (isUnknown(allowedRoute, announcedPrefix)) {
Expand All @@ -68,16 +70,16 @@ private RouteValidityState validate(AllowedRoute allowedRoute, AnnouncedRoute an
return RouteValidityState.VALID;
}

private boolean isUnknown(AllowedRoute allowedRoute, IpRange announcedPrefix) {
private static <T extends RoaPrefixData> boolean isUnknown(T allowedRoute, IpRange announcedPrefix) {
// non-intersecting or covering-aggregate
return !allowedRoute.getPrefix().contains(announcedPrefix);
}

private boolean isLengthInvalid(AllowedRoute allowedRoute, IpRange announcedPrefix) {
private static <T extends RoaPrefixData> boolean isLengthInvalid(T allowedRoute, IpRange announcedPrefix) {
return !(announcedPrefix.getPrefixLength() <= allowedRoute.getMaximumLength());
}

private boolean isAsnInvalid(AllowedRoute allowedRoute, AnnouncedRoute announcedRoute) {
private static <T extends RoaPrefixData, U extends RouteData> boolean isAsnInvalid(T allowedRoute, U announcedRoute) {
return !allowedRoute.getAsn().equals(announcedRoute.getOriginAsn());
}

Expand Down
Loading

0 comments on commit a69e654

Please sign in to comment.