Skip to content

Commit

Permalink
Generalize VRF leaking datamodel (#6516)
Browse files Browse the repository at this point in the history
Encapsulate in a separate object, allow leaking as BGP
  • Loading branch information
progwriter committed Dec 22, 2020
1 parent 8f816b6 commit 24a8dad
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public static class Builder {
@Nullable private Configuration _owner;
@Nonnull private Map<Long, EigrpProcess> _eigrpProcesses = ImmutableMap.of();

@Nonnull
private ImmutableList.Builder<VrfLeakingConfig> _vrfLeakingConfigs = ImmutableList.builder();

private Builder(Supplier<String> nameGenerator) {
_nameGenerator = nameGenerator;
}
Expand All @@ -56,6 +59,7 @@ public Vrf build() {
_owner.getVrfs().put(name, vrf);
}
vrf.setEigrpProcesses(_eigrpProcesses);
vrf.setVrfLeakConfigs(_vrfLeakingConfigs.build());
return vrf;
}

Expand All @@ -73,22 +77,26 @@ public Builder setOwner(@Nullable Configuration owner) {
_owner = owner;
return this;
}

public Builder addVrfLeakingConfig(@Nonnull VrfLeakingConfig c) {
_vrfLeakingConfigs.add(c);
return this;
}
}

private static final String PROP_BGP_PROCESS = "bgpProcess";
private static final String PROP_DESCRIPTION = "description";
private static final String PROP_HAS_ORIGINATING_SESSIONS = "hasOriginatingSessions";
private static final String PROP_FIREWALL_SESSION_VRF_INFO = "firewallSessionVrfInfo";
private static final String PROP_GENERATED_ROUTES = "aggregateRoutes";
private static final String PROP_CROSS_VRF_IMPORT_POLICY = "crossVrfImportPolicy";
private static final String PROP_CROSS_VRF_IMPORT_VRFS = "crossVrfImportVrfs";
private static final String PROP_ISIS_PROCESS = "isisProcess";
private static final String PROP_EIGRP_PROCESSES = "eigrpProcesses";
private static final String PROP_KERNEL_ROUTES = "kernelRoutes";
private static final String PROP_OSPF_PROCESS = "ospfProcess";
private static final String PROP_OSPF_PROCESSES = "ospfProcesses";
private static final String PROP_RIP_PROCESS = "ripProcess";
private static final String PROP_STATIC_ROUTES = "staticRoutes";
private static final String PROP_VRF_LEAKING_CONFIGS = "vrfLeakingConfigs";

public static @Nonnull Builder builder() {
return new Builder(null);
Expand All @@ -105,8 +113,6 @@ public Builder setOwner(@Nullable Configuration owner) {
private NavigableSet<GeneratedRoute6> _generatedIpv6Routes;
private NavigableSet<GeneratedRoute> _generatedRoutes;
private SortedMap<Long, EigrpProcess> _eigrpProcesses;
@Nullable private String _crossVrfImportPolicy;
@Nullable private List<String> _crossVrfImportVrfs;
private IsisProcess _isisProcess;
private SortedSet<KernelRoute> _kernelRoutes;
@Nonnull private SortedMap<String, OspfProcess> _ospfProcesses;
Expand All @@ -115,6 +121,7 @@ public Builder setOwner(@Nullable Configuration owner) {
private SortedSet<StaticRoute> _staticRoutes;
private Map<Integer, Layer2Vni> _layer2Vnis;
private Map<Integer, Layer3Vni> _layer3Vnis;
private List<VrfLeakingConfig> _vrfLeakConfigs;

public Vrf(@Nonnull String name) {
super(name);
Expand All @@ -127,6 +134,7 @@ public Vrf(@Nonnull String name) {
_staticRoutes = new TreeSet<>();
_layer2Vnis = ImmutableMap.of();
_layer3Vnis = ImmutableMap.of();
_vrfLeakConfigs = ImmutableList.of();
}

@JsonCreator
Expand Down Expand Up @@ -198,20 +206,6 @@ public Map<Long, EigrpProcess> getEigrpProcesses() {
return _eigrpProcesses;
}

/** Name of policy used to filter incoming routes leaked from other VRFs */
@Nullable
@JsonProperty(PROP_CROSS_VRF_IMPORT_POLICY)
public String getCrossVrfImportPolicy() {
return _crossVrfImportPolicy;
}

/** Names of other VRFs that leak routes into this one */
@Nullable
@JsonProperty(PROP_CROSS_VRF_IMPORT_VRFS)
public List<String> getCrossVrfImportVrfs() {
return _crossVrfImportVrfs;
}

/** IS-IS routing process for this VRF. */
@JsonProperty(PROP_ISIS_PROCESS)
public IsisProcess getIsisProcess() {
Expand Down Expand Up @@ -279,6 +273,22 @@ public void addLayer3Vni(Layer3Vni vni) {
.build();
}

@JsonProperty(PROP_VRF_LEAKING_CONFIGS)
public List<VrfLeakingConfig> getVrfLeakConfigs() {
return _vrfLeakConfigs;
}

public void addVrfLeakingConfig(@Nonnull VrfLeakingConfig c) {
_vrfLeakConfigs =
ImmutableList.<VrfLeakingConfig>builder().addAll(_vrfLeakConfigs).add(c).build();
}

/** For Builder (or Jackson) use only */
@JsonProperty(PROP_VRF_LEAKING_CONFIGS)
private void setVrfLeakConfigs(@Nullable List<VrfLeakingConfig> vrfLeakConfigs) {
_vrfLeakConfigs = ImmutableList.copyOf(firstNonNull(vrfLeakConfigs, ImmutableList.of()));
}

public void setAppliedRibGroups(Map<RoutingProtocol, RibGroup> appliedRibGroups) {
_appliedRibGroups = ImmutableSortedMap.copyOf(appliedRibGroups);
}
Expand Down Expand Up @@ -330,16 +340,6 @@ public void addEigrpProcess(@Nonnull EigrpProcess proc) {
.build();
}

@JsonProperty(PROP_CROSS_VRF_IMPORT_POLICY)
public void setCrossVrfImportPolicy(@Nonnull String crossVrfImportPolicy) {
_crossVrfImportPolicy = crossVrfImportPolicy;
}

@JsonProperty(PROP_CROSS_VRF_IMPORT_VRFS)
public void setCrossVrfImportVrfs(@Nonnull List<String> crossVrfImportVrfs) {
_crossVrfImportVrfs = ImmutableList.copyOf(crossVrfImportVrfs);
}

@JsonProperty(PROP_ISIS_PROCESS)
public void setIsisProcess(IsisProcess process) {
_isisProcess = process;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
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.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.MoreObjects;
import java.io.Serializable;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

/**
* Configuration responsible for VRF leaking. To successfully leak routes from one VRF to another,
* the destination VRF should contain one or more {@link VrfLeakingConfig} objects.
*
* <p>This object describes <em>how</em> to leak the routes, in particular:
*
* <ul>
* <li>from which source VRF to pull routes
* <li>what import policy to apply, if any
* <li>whether to leak routes as BGP or not
* </ul>
*
* <p><b>Note on BGP leaking: please see {@link #leakAsBgp()} for full explanation of semantics</b>
*/
@ParametersAreNonnullByDefault
public final class VrfLeakingConfig implements Serializable {

/**
* Name of the import policy to apply to imported routes when leaking. If {@code null} no policy
* is applied, all routes are allowed.
*/
@Nullable
@JsonProperty(PROP_IMPORT_POLICY)
public String getImportPolicy() {
return _importPolicy;
}

/**
* Name of the source VRF from which to copy routes.
*
* <p>If {@link #leakAsBgp()} is set, the source VRF <b>must</b> have a BGP process/RIBs.
*/
@Nonnull
@JsonProperty(PROP_IMPORT_FROM_VRF)
public String getImportFromVrf() {
return _importFromVrf;
}

/**
* Whether or not to leak routes between BGP RIBs or main RIBs.
*
* <p>For those familiar with vendor semantics: if set to {@code true}, leaking routes as BGP is
* equivalent to IOS vrf leaking which is done between BGP RIBs of different VRFs (on a real
* device, via VPNv4 address family). If set to {@code false}, the leaking process more closely
* follows the Juniper model, where routes are simply copied from the main RIB of one routing
* instance (read: VRF) into another, with appropriate src-VRF annotation.
*/
@JsonProperty(PROP_LEAK_AS_BGP)
public boolean leakAsBgp() {
return _leakAsBgp;
}

public static Builder builder() {
return new Builder();
}

@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof VrfLeakingConfig)) {
return false;
}
VrfLeakingConfig that = (VrfLeakingConfig) o;
return _importFromVrf.equals(that._importFromVrf)
&& Objects.equals(_importPolicy, that._importPolicy)
&& _leakAsBgp == that._leakAsBgp;
}

@Override
public int hashCode() {
return Objects.hash(_importPolicy, _importFromVrf, _leakAsBgp);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(VrfLeakingConfig.class)
.add("importPolicy", _importPolicy)
.add("importFromVrf", _importFromVrf)
.add("leakAsBgp", _leakAsBgp)
.toString();
}

@Nullable private final String _importPolicy;
@Nonnull private final String _importFromVrf;
private final boolean _leakAsBgp;

private VrfLeakingConfig(Builder builder) {
checkArgument(builder._importFromVrf != null, "VRF leaking config is missing import VRF");
_importPolicy = builder._importPolicy;
_importFromVrf = builder._importFromVrf;
_leakAsBgp = builder._leakAsBgp;
}

private static final String PROP_IMPORT_FROM_VRF = "importFromVrf";
private static final String PROP_IMPORT_POLICY = "importPolicy";
private static final String PROP_LEAK_AS_BGP = "leakAsBgp";

@JsonCreator
private static VrfLeakingConfig create(
@Nullable @JsonProperty(PROP_IMPORT_FROM_VRF) String importFromVrf,
@Nullable @JsonProperty(PROP_IMPORT_POLICY) String importPolicy,
@Nullable @JsonProperty(PROP_LEAK_AS_BGP) Boolean leakAsBgp) {
checkArgument(importFromVrf != null, String.format("Missing %s", PROP_IMPORT_FROM_VRF));
return builder()
.setImportFromVrf(importFromVrf)
.setImportPolicy(importPolicy)
.setLeakAsBgp(firstNonNull(leakAsBgp, Boolean.FALSE))
.build();
}

@ParametersAreNonnullByDefault
public static class Builder {

public Builder setImportPolicy(@Nullable String importPolicy) {
_importPolicy = importPolicy;
return this;
}

public Builder setImportFromVrf(String importFromVrf) {
_importFromVrf = importFromVrf;
return this;
}

public Builder setLeakAsBgp(boolean leakAsBgp) {
_leakAsBgp = leakAsBgp;
return this;
}

public VrfLeakingConfig build() {
return new VrfLeakingConfig(this);
}

@Nullable private String _importPolicy;
@Nullable private String _importFromVrf;
private boolean _leakAsBgp;

private Builder() {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.batfish.datamodel;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

import com.google.common.testing.EqualsTester;
import org.apache.commons.lang3.SerializationUtils;
import org.batfish.common.util.BatfishObjectMapper;
import org.batfish.datamodel.VrfLeakingConfig.Builder;
import org.junit.Test;

/** Tests of {@link VrfLeakingConfig} */
public class VrfLeakingConfigTest {

@Test
public void testJavaSerialization() {
VrfLeakingConfig val =
VrfLeakingConfig.builder()
.setImportFromVrf("vrf1")
.setImportPolicy("policy")
.setLeakAsBgp(false)
.build();
assertThat(SerializationUtils.clone(val), equalTo(val));
}

@Test
public void testJsonSerialization() {
VrfLeakingConfig val =
VrfLeakingConfig.builder()
.setImportFromVrf("vrf1")
.setImportPolicy("policy")
.setLeakAsBgp(false)
.build();
assertThat(BatfishObjectMapper.clone(val, VrfLeakingConfig.class), equalTo(val));
}

@Test
public void testEquals() {
Builder b =
VrfLeakingConfig.builder()
.setImportFromVrf("vrf1")
.setImportPolicy("policy")
.setLeakAsBgp(false);
VrfLeakingConfig val = b.build();
new EqualsTester()
.addEqualityGroup(val, val, b.build())
.addEqualityGroup(b.setImportFromVrf("vrf2").build())
.addEqualityGroup(b.setImportPolicy("policy2").build())
.addEqualityGroup(b.setLeakAsBgp(true).build())
.addEqualityGroup(new Object())
.testEquals();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.batfish.datamodel.StaticRoute;
import org.batfish.datamodel.Topology;
import org.batfish.datamodel.Vrf;
import org.batfish.datamodel.VrfLeakingConfig;
import org.batfish.datamodel.bgp.BgpTopology;
import org.batfish.datamodel.dataplane.rib.RibGroup;
import org.batfish.datamodel.dataplane.rib.RibId;
Expand Down Expand Up @@ -373,17 +374,15 @@ void initQueuesAndDeltaBuilders(TopologyContext topologyContext) {
* their main ribs to {@link #_crossVrfIncomingRoutes}.
*/
void initCrossVrfImports() {
if (_vrf.getCrossVrfImportPolicy() == null || _vrf.getCrossVrfImportVrfs() == null) {
return;
}
for (String vrfToImport : _vrf.getCrossVrfImportVrfs()) {
VirtualRouter exportingVR = _node.getVirtualRouterOrThrow(vrfToImport);
CrossVrfEdgeId otherVrfToOurRib = new CrossVrfEdgeId(vrfToImport, RibId.DEFAULT_RIB_NAME);
for (VrfLeakingConfig leakConfig : _vrf.getVrfLeakConfigs()) {
String importFromVrf = leakConfig.getImportFromVrf();
VirtualRouter exportingVR = _node.getVirtualRouterOrThrow(importFromVrf);
CrossVrfEdgeId otherVrfToOurRib = new CrossVrfEdgeId(importFromVrf, RibId.DEFAULT_RIB_NAME);
enqueueCrossVrfRoutes(
otherVrfToOurRib,
// TODO Will need to update once support is added for cross-VRF export policies
exportingVR._mainRib.getTypedRoutes().stream().map(RouteAdvertisement::new),
_vrf.getCrossVrfImportPolicy());
leakConfig.getImportPolicy());
}
}

Expand Down Expand Up @@ -1253,17 +1252,18 @@ void processCrossVrfRoutes() {
* routes in {@link #_crossVrfIncomingRoutes}.
*/
void queueCrossVrfImports() {
if (_vrf.getCrossVrfImportPolicy() == null || _vrf.getCrossVrfImportVrfs() == null) {
return;
}
for (String vrfToImport : _vrf.getCrossVrfImportVrfs()) {
VirtualRouter exportingVR = _node.getVirtualRouterOrThrow(vrfToImport);
CrossVrfEdgeId otherVrfToOurRib = new CrossVrfEdgeId(vrfToImport, RibId.DEFAULT_RIB_NAME);
for (VrfLeakingConfig leakConfig : _vrf.getVrfLeakConfigs()) {
if (leakConfig.leakAsBgp()) {
continue;
}
String importFromVrf = leakConfig.getImportFromVrf();
VirtualRouter exportingVR = _node.getVirtualRouterOrThrow(importFromVrf);
CrossVrfEdgeId otherVrfToOurRib = new CrossVrfEdgeId(importFromVrf, RibId.DEFAULT_RIB_NAME);
enqueueCrossVrfRoutes(
otherVrfToOurRib,
// TODO Will need to update once support is added for cross-VRF export policies
exportingVR._mainRibDeltaPrevRound.getActions(),
_vrf.getCrossVrfImportPolicy());
leakConfig.getImportPolicy());
}
}

Expand Down

0 comments on commit 24a8dad

Please sign in to comment.