Skip to content

Commit

Permalink
EIGRP: add metric version and plumb throughout (#6532)
Browse files Browse the repository at this point in the history
Different implementations of EIGRP compute the cost with greater and lesser
degrees of precision. Add a setting to each EIGRP process to indicate which
cost implementation it uses, and add the classic metric computations for NX-OS.
  • Loading branch information
dhalperi committed Dec 30, 2020
1 parent 66efc18 commit 2c14018
Show file tree
Hide file tree
Showing 25 changed files with 293 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.hash;
import static java.util.Objects.requireNonNull;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand All @@ -11,6 +10,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.batfish.datamodel.eigrp.EigrpMetric;
import org.batfish.datamodel.eigrp.EigrpMetricVersion;

/** Represents an external EIGRP route */
public class EigrpExternalRoute extends EigrpRoute {
Expand All @@ -28,11 +28,21 @@ private EigrpExternalRoute(
long destinationAsn,
@Nullable Ip nextHopIp,
@Nonnull EigrpMetric metric,
@Nonnull EigrpMetricVersion metricVersion,
long processAsn,
long tag,
boolean nonForwarding,
boolean nonRouting) {
super(admin, network, nextHopIp, metric, processAsn, tag, nonForwarding, nonRouting);
super(
admin,
network,
nextHopIp,
metric,
metricVersion,
processAsn,
tag,
nonForwarding,
nonRouting);
_destinationAsn = destinationAsn;
}

Expand All @@ -43,15 +53,26 @@ private static EigrpExternalRoute create(
@Nullable @JsonProperty(PROP_NETWORK) Prefix network,
@Nullable @JsonProperty(PROP_NEXT_HOP_IP) Ip nextHopIp,
@Nullable @JsonProperty(PROP_EIGRP_METRIC) EigrpMetric metric,
@Nullable @JsonProperty(PROP_EIGRP_METRIC_VERSION) EigrpMetricVersion metricVersion,
@Nullable @JsonProperty(PROP_PROCESS_ASN) Long processAsn,
@Nullable @JsonProperty(PROP_TAG) Long tag) {
checkArgument(admin != null, "EIGRP route: missing %s", PROP_ADMINISTRATIVE_COST);
checkArgument(destinationAsn != null, "EIGRP route: missing %s", PROP_DESTINATION_ASN);
checkArgument(metric != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC);
checkArgument(metricVersion != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC_VERSION);
checkArgument(processAsn != null, "EIGRP route: missing %s", PROP_PROCESS_ASN);
checkArgument(tag != null, "EIGRP route: missing %s", PROP_TAG);
return new EigrpExternalRoute(
network, admin, destinationAsn, nextHopIp, metric, processAsn, tag, false, false);
network,
admin,
destinationAsn,
nextHopIp,
metric,
metricVersion,
processAsn,
tag,
false,
false);
}

@JsonProperty(PROP_DESTINATION_ASN)
Expand Down Expand Up @@ -79,13 +100,16 @@ public EigrpExternalRoute build() {
checkArgument(getNetwork() != null, "EIGRP route: missing %s", PROP_NETWORK);
checkArgument(_destinationAsn != null, "EIGRP route: missing %s", PROP_DESTINATION_ASN);
checkArgument(_eigrpMetric != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC);
checkArgument(
_eigrpMetricVersion != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC_VERSION);
checkArgument(_processAsn != null, "EIGRP route: missing %s", PROP_PROCESS_ASN);
return new EigrpExternalRoute(
getNetwork(),
getAdmin(),
_destinationAsn,
getNextHopIp(),
requireNonNull(_eigrpMetric),
_eigrpMetric,
_eigrpMetricVersion,
_processAsn,
getTag(),
getNonForwarding(),
Expand Down Expand Up @@ -115,6 +139,7 @@ public Builder toBuilder() {
// EigrpExternalRoute properties
.setDestinationAsn(getDestinationAsn())
.setEigrpMetric(getEigrpMetric())
.setEigrpMetricVersion(getEigrpMetricVersion())
.setProcessAsn(getProcessAsn());
}

Expand All @@ -130,6 +155,7 @@ public boolean equals(Object obj) {
return _admin == rhs._admin
&& Objects.equals(_destinationAsn, rhs._destinationAsn)
&& _metric.equals(rhs._metric)
&& _metricVersion == rhs._metricVersion
&& _network.equals(rhs._network)
&& _nextHopIp.equals(rhs._nextHopIp)
&& _processAsn == rhs._processAsn
Expand All @@ -149,6 +175,7 @@ public final int hashCode() {
getNonRouting(),
_destinationAsn,
_metric,
_metricVersion,
_processAsn);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.batfish.datamodel.eigrp.EigrpMetric;
import org.batfish.datamodel.eigrp.EigrpMetricVersion;

/** Represents an internal EIGRP route */
public class EigrpInternalRoute extends EigrpRoute {
Expand All @@ -19,10 +20,20 @@ private EigrpInternalRoute(
@Nullable Prefix network,
@Nullable Ip nextHopIp,
@Nonnull EigrpMetric metric,
@Nonnull EigrpMetricVersion metricVersion,
long tag,
boolean nonForwarding,
boolean nonRouting) {
super(admin, network, nextHopIp, metric, processAsn, tag, nonForwarding, nonRouting);
super(
admin,
network,
nextHopIp,
metric,
metricVersion,
processAsn,
tag,
nonForwarding,
nonRouting);
}

@JsonCreator
Expand All @@ -32,12 +43,15 @@ private static EigrpInternalRoute create(
@Nullable @JsonProperty(PROP_NETWORK) Prefix network,
@Nullable @JsonProperty(PROP_NEXT_HOP_IP) Ip nextHopIp,
@Nullable @JsonProperty(PROP_EIGRP_METRIC) EigrpMetric metric,
@Nullable @JsonProperty(PROP_EIGRP_METRIC_VERSION) EigrpMetricVersion metricVersion,
@Nullable @JsonProperty(PROP_TAG) Long tag) {
checkArgument(admin != null, "EIGRP rooute: missing %s", PROP_ADMINISTRATIVE_COST);
checkArgument(admin != null, "EIGRP route: missing %s", PROP_ADMINISTRATIVE_COST);
checkArgument(metric != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC);
checkArgument(metricVersion != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC_VERSION);
checkArgument(processAsn != null, "EIGRP route: missing %s", PROP_PROCESS_ASN);
checkArgument(tag != null, "EIGRP route: missing %s", PROP_TAG);
return new EigrpInternalRoute(admin, processAsn, network, nextHopIp, metric, tag, false, false);
return new EigrpInternalRoute(
admin, processAsn, network, nextHopIp, metric, metricVersion, tag, false, false);
}

public static Builder builder() {
Expand All @@ -58,6 +72,8 @@ private Builder() {}
public EigrpInternalRoute build() {
checkArgument(getNetwork() != null, "EIGRP route: missing %s", PROP_NETWORK);
checkArgument(_eigrpMetric != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC);
checkArgument(
_eigrpMetricVersion != null, "EIGRP route: missing %s", PROP_EIGRP_METRIC_VERSION);
checkArgument(_processAsn != null, "EIGRP route: missing %s", PROP_PROCESS_ASN);
checkArgument(
getMetric() == 0, "EIGRP route: cannot set metric directly, use setEigrpMetric instead");
Expand All @@ -67,6 +83,7 @@ public EigrpInternalRoute build() {
getNetwork(),
getNextHopIp(),
_eigrpMetric,
_eigrpMetricVersion,
getTag(),
getNonForwarding(),
getNonRouting());
Expand Down Expand Up @@ -94,6 +111,7 @@ public Builder toBuilder() {
.setNonRouting(getNonRouting())
// EigrpInternalRoute properties
.setEigrpMetric(getEigrpMetric())
.setEigrpMetricVersion(getEigrpMetricVersion())
.setProcessAsn(getProcessAsn());
}

Expand All @@ -113,6 +131,7 @@ public boolean equals(Object obj) {
&& _tag == rhs._tag
&& _processAsn == rhs._processAsn
&& _metric.equals(rhs._metric)
&& _metricVersion == rhs._metricVersion
&& getNonForwarding() == rhs.getNonForwarding()
&& getNonRouting() == rhs.getNonRouting();
}
Expand All @@ -126,6 +145,7 @@ public final int hashCode() {
_tag,
_processAsn,
_metric,
_metricVersion,
getNonForwarding(),
getNonRouting());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package org.batfish.datamodel;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.primitives.UnsignedLong;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.batfish.datamodel.eigrp.EigrpMetric;
import org.batfish.datamodel.eigrp.EigrpMetricVersion;

/** Represents an EIGRP route, internal or external */
public abstract class EigrpRoute extends AbstractRoute {

static final String PROP_EIGRP_METRIC = "eigrp-metric";
static final String PROP_EIGRP_METRIC_VERSION = "eigrp-metric-version";
static final String PROP_PROCESS_ASN = "process-asn";

protected final int _admin;
@Nonnull protected final EigrpMetric _metric;
@Nonnull protected final EigrpMetricVersion _metricVersion;
@Nonnull protected final Ip _nextHopIp;

/** AS number of the EIGRP process that installed this route in the RIB */
Expand All @@ -27,22 +29,23 @@ public abstract class EigrpRoute extends AbstractRoute {
int admin,
Prefix network,
@Nullable Ip nextHopIp,
@Nullable EigrpMetric metric,
@Nonnull EigrpMetric metric,
@Nonnull EigrpMetricVersion metricVersion,
long processAsn,
long tag,
boolean nonForwarding,
boolean nonRouting) {
super(network, admin, tag, nonRouting, nonForwarding);
checkArgument(metric != null, "Cannot create EIGRP route: missing %s", PROP_EIGRP_METRIC);
_admin = admin;
_metric = metric;
_metricVersion = metricVersion;
_nextHopIp = firstNonNull(nextHopIp, Route.UNSET_ROUTE_NEXT_HOP_IP);
_processAsn = processAsn;
}

@JsonIgnore
public final UnsignedLong getCompositeCost() {
return _metric.cost();
return _metric.cost(_metricVersion);
}

@JsonProperty(PROP_EIGRP_METRIC)
Expand All @@ -51,9 +54,15 @@ public final EigrpMetric getEigrpMetric() {
return _metric;
}

@JsonProperty(PROP_EIGRP_METRIC_VERSION)
@Nonnull
public final EigrpMetricVersion getEigrpMetricVersion() {
return _metricVersion;
}

@Override
public final Long getMetric() {
return _metric.ribMetric();
return _metric.ribMetric(_metricVersion);
}

@Nonnull
Expand Down Expand Up @@ -82,6 +91,7 @@ public abstract static class Builder<B extends Builder<B, R>, R extends EigrpRou
extends AbstractRouteBuilder<B, R> {
@Nullable protected Long _destinationAsn;
@Nullable protected EigrpMetric _eigrpMetric;
@Nullable protected EigrpMetricVersion _eigrpMetricVersion;
@Nullable protected Long _processAsn;

public B setDestinationAsn(@Nonnull Long destinationAsn) {
Expand All @@ -94,6 +104,11 @@ public B setEigrpMetric(@Nonnull EigrpMetric metric) {
return getThis();
}

public B setEigrpMetricVersion(@Nonnull EigrpMetricVersion version) {
_eigrpMetricVersion = version;
return getThis();
}

public B setProcessAsn(@Nullable Long processAsn) {
_processAsn = processAsn;
return getThis();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,18 @@ public short getK5() {
}

@Override
public UnsignedLong cost() {
public UnsignedLong cost(EigrpMetricVersion version) {
switch (version) {
case V1:
return costV1();
case V2:
return costV2();
default:
throw new IllegalArgumentException("Unsupported version " + version);
}
}

private UnsignedLong costV1() {
checkState(_values.getBandwidth() != null, "Cannot calculate cost before bandwidth is set");
long scaledBw = BANDWIDTH_FACTOR / _values.getBandwidth();
long metric =
Expand All @@ -93,9 +104,24 @@ public UnsignedLong cost() {
return UnsignedLong.valueOf(metric);
}

private UnsignedLong costV2() {
checkState(_values.getBandwidth() != null, "Cannot calculate cost before bandwidth is set");
long scaledBw = 256L * BANDWIDTH_FACTOR / _values.getBandwidth();
long metricBw = (_k1 * scaledBw) + (_k2 * scaledBw) / (256 - _values.getEffectiveBandwidth());
long metricDelay =
// Scale delay from picoseconds to 10s of microseconds
// Do that first, to avoid overflow
256L * _k3 * (_values.getDelay() / PICO_TO_TENS_OF_MS_FACTOR);
long metric = metricBw + metricDelay;
if (_k5 != 0) {
metric = metric * ((long) _k5 / ((long) _values.getReliability() + _k4));
}
return UnsignedLong.valueOf(metric);
}

@Override
public long ribMetric() {
return cost().longValue();
public long ribMetric(EigrpMetricVersion version) {
return cost(version).longValue();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
})
public interface EigrpMetric extends Serializable {

/** Return metric as a single value, used for route preference tie breaking. */
UnsignedLong cost();
/** Return the metric as a single value, used for route preference tie breaking. */
UnsignedLong cost(EigrpMetricVersion version);

/**
* Return metric as a single value, scaled as needed to fit into a 4-byte value. Used for route
* preference tie breaking.
* Return metric as a single value, scaled as needed to fit into a 4-byte value. Used as a metric
* expected to be seen in the main RIB.
*/
long ribMetric();
long ribMetric(EigrpMetricVersion version);

/**
* Check if this metric and {@code other} are compatible, i.e., have the same type and k values
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.batfish.datamodel.eigrp;

/**
* Identifies how to summarize an {@link EigrpMetric} as a single value.
*
* <p>Is implementation-dependent. E.g., IOS-XE uses V1 and NX-OS uses V2.
*/
public enum EigrpMetricVersion {
/** IOS-XE -like. */
V1,
/** NX-OS -like. Higher precision than IOS-XE. */
V2,
}

0 comments on commit 2c14018

Please sign in to comment.