Skip to content

Commit

Permalink
FRR: convert ospf area range (#6665)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhalperi committed Mar 1, 2021
1 parent 1faff03 commit d149c5b
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 14 deletions.
Expand Up @@ -845,10 +845,7 @@ public void exitSiip_address(Siip_addressContext ctx) {

@Override
public void enterS_router_ospf(S_router_ospfContext ctx) {
if (_frr.getOspfProcess() == null) {
_frr.setOspfProcess(new OspfProcess());
}
_currentOspfVrf = _frr.getOspfProcess().getDefaultVrf();
_currentOspfVrf = _frr.getOrCreateOspfProcess().getDefaultVrf();
}

@Override
Expand Down
Expand Up @@ -5,7 +5,6 @@
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static org.batfish.common.util.CollectionUtil.toImmutableSortedMap;
import static org.batfish.datamodel.Configuration.DEFAULT_VRF_NAME;
import static org.batfish.datamodel.MultipathEquivalentAsPathMatchMode.EXACT_PATH;
import static org.batfish.datamodel.MultipathEquivalentAsPathMatchMode.PATH_LENGTH;
Expand All @@ -24,6 +23,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
Expand Down Expand Up @@ -82,6 +82,7 @@
import org.batfish.datamodel.bgp.community.Community;
import org.batfish.datamodel.bgp.community.ExtendedCommunity;
import org.batfish.datamodel.ospf.OspfArea;
import org.batfish.datamodel.ospf.OspfAreaSummary;
import org.batfish.datamodel.ospf.OspfInterfaceSettings;
import org.batfish.datamodel.ospf.OspfMetricType;
import org.batfish.datamodel.routing_policy.Common;
Expand Down Expand Up @@ -202,6 +203,10 @@ public static String computeOspfExportPolicyName(String vrfName) {
return String.format("~OSPF_EXPORT_POLICY:%s~", vrfName);
}

public static String computeOspfAreaRangeFilterName(String vrfName, long area) {
return String.format("~OSPF_AREA_RANGE:%s:%s~", vrfName, area);
}

/**
* Infers the peer Ip for the interface. This is doable only if the interface has exactly one
* (primary) address that is a /30 or /31. For a /30, the address must NOT be the first or last
Expand Down Expand Up @@ -1282,7 +1287,7 @@ static org.batfish.datamodel.ospf.OspfProcess toOspfProcess(
}

addOspfInterfaces(vsConfig, vrfInterfaces, proc.getProcessId(), w);
proc.setAreas(computeOspfAreas(vsConfig, vrfInterfaces.keySet()));
proc.setAreas(computeOspfAreas(c, vsConfig, ospfVrf, vrfInterfaces.keySet()));

// Handle Max Metric Router LSA
if (firstNonNull(vsConfig.getOspfProcess().getMaxMetricRouterLsa(), Boolean.FALSE)) {
Expand Down Expand Up @@ -1425,7 +1430,10 @@ static void addOspfInterfaces(

@VisibleForTesting
static SortedMap<Long, OspfArea> computeOspfAreas(
CumulusConcatenatedConfiguration vsConfig, Collection<String> vrfIfaceNames) {
Configuration c,
CumulusConcatenatedConfiguration vsConfig,
OspfVrf ospfVrf,
Collection<String> vrfIfaceNames) {
Map<Long, List<String>> areaInterfaces =
vrfIfaceNames.stream()
.filter(
Expand All @@ -1438,10 +1446,60 @@ static SortedMap<Long, OspfArea> computeOspfAreas(
iface -> vsConfig.getOspfInterface(iface).get().getOspfArea(),
mapping(Function.identity(), Collectors.toList())));

return toImmutableSortedMap(
areaInterfaces,
Entry::getKey,
e -> OspfArea.builder().setNumber(e.getKey()).addInterfaces(e.getValue()).build());
// Ensure that the VRF-level OSPF config has each area used.
areaInterfaces.keySet().forEach(ospfVrf::getOrCreateArea);

// Now convert each area
ImmutableSortedMap.Builder<Long, OspfArea> areas = ImmutableSortedMap.naturalOrder();
ospfVrf
.getAreas()
.values()
.forEach(
vrfArea -> {
long num = vrfArea.getArea();
List<String> interfaces = areaInterfaces.get(num);
if (interfaces.isEmpty()) {
// No interfaces, skip area.
return;
}
OspfArea area = convertArea(c, ospfVrf.getVrfName(), vrfArea, interfaces);
areas.put(area.getAreaNumber(), area);
});
return areas.build();
}

private static @Nonnull OspfArea convertArea(
Configuration c,
String vrfName,
org.batfish.representation.cumulus.OspfArea vsArea,
Collection<String> interfaces) {
OspfArea.Builder ret = OspfArea.builder().setNumber(vsArea.getArea()).addInterfaces(interfaces);

// Handle OSPF Area Range conversion into VI OspfAreaSummary. It has two steps:
// 1. convert OspfAreaRange to OspfAreaSummary
// 2. add a RouteFilterList to force summarization and block more specific routes.
if (!vsArea.getRanges().isEmpty()) {
ImmutableList.Builder<RouteFilterLine> lines =
ImmutableList.builderWithExpectedSize(vsArea.getRanges().size() + 1);
for (OspfAreaRange range : vsArea.getRanges().values()) {
// OSPF costs are only 16-bit, but the VI metric is a long, for other protocols that support
// that. Upconvert if not null.
Prefix prefix = range.getRange();
Long cost = range.getCost() == null ? null : range.getCost().longValue();
OspfAreaSummary summary = new OspfAreaSummary(true, cost);
ret.addSummary(prefix, summary);
lines.add(new RouteFilterLine(LineAction.DENY, PrefixRange.moreSpecificThan(prefix)));
}
// Anything not summarized is permitted.
lines.add(new RouteFilterLine(LineAction.PERMIT, PrefixRange.ALL));
RouteFilterList summaryFilter =
new RouteFilterList(
computeOspfAreaRangeFilterName(vrfName, vsArea.getArea()), lines.build());
c.getRouteFilterLists().put(summaryFilter.getName(), summaryFilter);
ret.setSummaryFilter(summaryFilter.getName());
}

return ret.build();
}

@Nullable
Expand Down
Expand Up @@ -64,6 +64,13 @@ public void setBgpProcess(@Nullable BgpProcess bgpProcess) {
_bgpProcess = bgpProcess;
}

public @Nonnull OspfProcess getOrCreateOspfProcess() {
if (_ospfProcess == null) {
_ospfProcess = new OspfProcess();
}
return _ospfProcess;
}

@Nullable
public OspfProcess getOspfProcess() {
return _ospfProcess;
Expand Down
@@ -1,6 +1,7 @@
package org.batfish.representation.cumulus;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
Expand All @@ -27,6 +28,10 @@ public OspfVrf(String name) {
return _areas.get(area);
}

public @Nonnull Map<Long, OspfArea> getAreas() {
return Collections.unmodifiableMap(_areas);
}

@Nullable
public Ip getRouterId() {
return _routerId;
Expand Down
Expand Up @@ -8,6 +8,7 @@
import static org.batfish.datamodel.matchers.MapMatchers.hasKeys;
import static org.batfish.datamodel.routing_policy.Environment.Direction.OUT;
import static org.batfish.grammar.cumulus_frr.CumulusFrrConfigurationBuilder.nextMultipleOfFive;
import static org.batfish.representation.cumulus.CumulusConversions.computeOspfAreaRangeFilterName;
import static org.batfish.representation.cumulus.CumulusRoutingProtocol.CONNECTED;
import static org.batfish.representation.cumulus.CumulusRoutingProtocol.OSPF;
import static org.batfish.representation.cumulus.CumulusRoutingProtocol.STATIC;
Expand All @@ -21,6 +22,7 @@
import static org.batfish.representation.cumulus.CumulusStructureUsage.ROUTE_MAP_MATCH_COMMUNITY_LIST;
import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anEmptyMap;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -77,11 +79,14 @@
import org.batfish.datamodel.OriginType;
import org.batfish.datamodel.OspfExternalType2Route;
import org.batfish.datamodel.Prefix;
import org.batfish.datamodel.RouteFilterList;
import org.batfish.datamodel.RoutingProtocol;
import org.batfish.datamodel.StaticRoute.Builder;
import org.batfish.datamodel.SubRange;
import org.batfish.datamodel.answers.ConvertConfigurationAnswerElement;
import org.batfish.datamodel.bgp.community.StandardCommunity;
import org.batfish.datamodel.ospf.OspfAreaSummary;
import org.batfish.datamodel.ospf.OspfProcess;
import org.batfish.datamodel.routing_policy.RoutingPolicy;
import org.batfish.datamodel.routing_policy.expr.LiteralLong;
import org.batfish.grammar.BatfishParseTreeWalker;
Expand Down Expand Up @@ -1888,6 +1893,48 @@ public void testRouterOspfAreaRange() {
assertThat(area5.getRange(prefix).getCost(), nullValue());
}

@Test
public void testRouterOspfAreaRangeConversion() {
parse(
"interface swp1\n"
+ " ip address 1.2.3.4/5\n"
+ " ip ospf area 1.1.1.0\n"
+ "\n"
+ "interface swp2\n"
+ " ip address 2.3.4.5/6\n"
+ " ip ospf area 5\n"
+ "\n"
+ "router ospf\n"
+ " area 1.1.1.0 range 1.255.0.0/17 cost 10\n");
long area1110 = Ip.parse("1.1.1.0").asLong();
Configuration c = _config.toVendorIndependentConfigurations().get(0);
assertThat(c.getDefaultVrf().getOspfProcesses(), aMapWithSize(1));
OspfProcess proc = Iterables.getOnlyElement(c.getDefaultVrf().getOspfProcesses().values());
assertThat(proc.getAreas(), hasKeys(5L, area1110));
{
org.batfish.datamodel.ospf.OspfArea area = proc.getAreas().get(5L);
assertThat(area.getSummaries(), anEmptyMap());
assertThat(area.getSummaryFilter(), nullValue());
}
{
org.batfish.datamodel.ospf.OspfArea area = proc.getAreas().get(area1110);
assertThat(area.getSummaries(), hasKeys(Prefix.parse("1.255.0.0/17")));
OspfAreaSummary summary = Iterables.getOnlyElement(area.getSummaries().values());
assertThat(summary.getAdvertised(), equalTo(true));
assertThat(summary.getMetric(), equalTo(10L));
String name = computeOspfAreaRangeFilterName(c.getDefaultVrf().getName(), area1110);
assertThat(area.getSummaryFilter(), equalTo(name));
assertThat(c.getRouteFilterLists(), hasKey(name));
RouteFilterList filter = c.getRouteFilterLists().get(name);
assertTrue(filter.permits(Prefix.parse("1.255.0.0/17"))); // prefix itself should be permitted
assertFalse(
filter.permits(
Prefix.parse("1.255.64.0/18"))); // more specific, even with diff prefix, denied
assertTrue(filter.permits(Prefix.parse("10.0.0.0/18"))); // more specific, not overlapping
assertTrue(filter.permits(Prefix.parse("1.255.0.0/16"))); // less specific
}
}

@Test
public void testRouterOspfNetwork() {
parse(
Expand Down
Expand Up @@ -1766,9 +1766,12 @@ public void testComputeOspfProcess_HasArea() {
.getOrCreateInterface("iface")
.getOrCreateOspf()
.setOspfArea(1L);
OspfVrf vsVrf =
concatenatedConfiguration.getFrrConfiguration().getOrCreateOspfProcess().getDefaultVrf();
Configuration c = new Configuration("c", ConfigurationFormat.CUMULUS_CONCATENATED);

SortedMap<Long, OspfArea> areas =
computeOspfAreas(concatenatedConfiguration, ImmutableList.of("iface"));
computeOspfAreas(c, concatenatedConfiguration, vsVrf, ImmutableList.of("iface"));
assertThat(
areas,
equalTo(
Expand All @@ -1781,19 +1784,25 @@ public void testComputeOspfProcess_NoArea() {
CumulusConcatenatedConfiguration concatenatedConfiguration =
new CumulusConcatenatedConfiguration();
concatenatedConfiguration.getFrrConfiguration().getOrCreateInterface("iface").getOrCreateOspf();
OspfVrf vsVrf =
concatenatedConfiguration.getFrrConfiguration().getOrCreateOspfProcess().getDefaultVrf();
Configuration c = new Configuration("c", ConfigurationFormat.CUMULUS_CONCATENATED);

SortedMap<Long, OspfArea> areas =
computeOspfAreas(concatenatedConfiguration, ImmutableList.of("iface"));
computeOspfAreas(c, concatenatedConfiguration, vsVrf, ImmutableList.of("iface"));
assertThat(areas, equalTo(ImmutableSortedMap.of()));
}

@Test
public void testComputeOspfProcess_NoInterface() {
CumulusConcatenatedConfiguration concatenatedConfiguration =
new CumulusConcatenatedConfiguration();
OspfVrf vsVrf =
concatenatedConfiguration.getFrrConfiguration().getOrCreateOspfProcess().getDefaultVrf();
Configuration c = new Configuration("c", ConfigurationFormat.CUMULUS_CONCATENATED);

SortedMap<Long, OspfArea> areas =
computeOspfAreas(concatenatedConfiguration, ImmutableList.of("iface"));
computeOspfAreas(c, concatenatedConfiguration, vsVrf, ImmutableList.of("iface"));
assertThat(areas, equalTo(ImmutableSortedMap.of()));
}

Expand Down

0 comments on commit d149c5b

Please sign in to comment.