public static AclIpSpace of(Iterable<AclIpSpaceLine> lines) { Builder builder = builder(); lines.forEach(builder::then); return builder.build(); }
private static @Nullable IpSpace unionNonnull(IpSpace... ipSpaces) { if (ipSpaces.length == 0) { return null; } else if (ipSpaces.length == 1) { return ipSpaces[0]; } else { return builder().thenPermitting(ipSpaces).build(); } }
/** * Set-theoretic difference between two IpSpaces.<br> * If both arguments are {@code null}, returns {@code null}.<br> * If just {@code ipSpace1} is {@code null}, treat it as {@link UniverseIpSpace}.<br> * If just {@code ipSpace2} is {@code null}, treat it as {@link EmptyIpSpace}. */ public static @Nullable IpSpace difference( @Nullable IpSpace ipSpace1, @Nullable IpSpace ipSpace2) { if (ipSpace1 == null && ipSpace2 == null) { return null; } else if (ipSpace2 == null) { return ipSpace1; } return builder() .thenRejecting(ipSpace2) .thenPermitting(firstNonNull(ipSpace1, UniverseIpSpace.INSTANCE)) .build(); }
/** * Returns an {@link IpSpace} representing the space of all IPs whose magnitude is between that of * {@code low} and {@code high} (inclusive). */ public static @Nonnull IpSpace range(Ip low, Ip high) { checkArgument( low.asLong() <= high.asLong(), "Invalid range: low IP must be <= high IP, but received low=%s and high=%s", low, high); ImmutableList.Builder<Prefix> prefixes = ImmutableList.builder(); collectPrefixes(low, high, prefixes); return AclIpSpace.builder() .thenPermitting(prefixes.build().stream().map(Prefix::toIpSpace)) .build(); }
/** * Invert a mapping from Ip to VRF owners (Ip -> host name -> VRF name) and combine all IPs * owned by each VRF into an IpSpace. */ public static Map<String, Map<String, IpSpace>> computeVrfOwnedIpSpaces( Map<Ip, Map<String, Set<String>>> ipVrfOwners) { Map<String, Map<String, AclIpSpace.Builder>> builders = new HashMap<>(); ipVrfOwners.forEach( (ip, ipNodeVrfs) -> ipNodeVrfs.forEach( (node, vrfs) -> vrfs.forEach( vrf -> builders .computeIfAbsent(node, k -> new HashMap<>()) .computeIfAbsent(vrf, k -> AclIpSpace.builder()) .thenPermitting(ip.toIpSpace())))); return CommonUtil.toImmutableMap( builders, Entry::getKey, /* node */ nodeEntry -> CommonUtil.toImmutableMap( nodeEntry.getValue(), Entry::getKey, /* vrf */ vrfEntry -> vrfEntry.getValue().build())); }
private static @Nullable IpSpace intersection(Spliterator<IpSpace> ipSpaces) { IpSpace[] nonNullSpaces = StreamSupport.stream(ipSpaces, false).filter(Objects::nonNull).toArray(IpSpace[]::new); if (nonNullSpaces.length == 0) { // all null return null; } IpSpace[] nonUniverseSpaces = Arrays.stream(nonNullSpaces) .filter(ipSpace -> ipSpace != UniverseIpSpace.INSTANCE) .toArray(IpSpace[]::new); if (nonUniverseSpaces.length == 0) { // all UniverseIpSpace return UniverseIpSpace.INSTANCE; } if (nonUniverseSpaces.length == 1) { return nonUniverseSpaces[0]; } // complement each concrete space. for (int i = 0; i < nonUniverseSpaces.length; i++) { if (nonUniverseSpaces[i] == EmptyIpSpace.INSTANCE) { return EmptyIpSpace.INSTANCE; } nonUniverseSpaces[i] = nonUniverseSpaces[i].complement(); } return builder() .thenRejecting(nonUniverseSpaces) .thenPermitting(UniverseIpSpace.INSTANCE) .build(); }
@VisibleForTesting Map<Edge, IpSpace> computeArpTrueEdge() { try (ActiveSpan span = GlobalTracer.get().buildSpan("ForwardingAnalysisImpl.computeArpTrueEdge").startActive()) { assert span != null; // avoid unused warning return Sets.union(_arpTrueEdgeDestIp.keySet(), _arpTrueEdgeNextHopIp.keySet()).stream() .collect( ImmutableMap.toImmutableMap( Function.identity(), edge -> { AclIpSpace.Builder ipSpace = AclIpSpace.builder(); IpSpace dstIp = _arpTrueEdgeDestIp.get(edge); if (dstIp != null) { ipSpace.thenPermitting(dstIp); } IpSpace nextHopIp = _arpTrueEdgeNextHopIp.get(edge); if (nextHopIp != null) { ipSpace.thenPermitting(nextHopIp); } return ipSpace.build(); })); } }
@VisibleForTesting Map<String, Map<String, IpSpace>> computeSomeoneReplies(Topology topology) { try (ActiveSpan span = GlobalTracer.get() .buildSpan("ForwardingAnalysisImpl.computeSomeoneReplies") .startActive()) { assert span != null; // avoid unused warning Map<String, Map<String, AclIpSpace.Builder>> someoneRepliesByNode = new HashMap<>(); topology .getEdges() .forEach( edge -> someoneRepliesByNode .computeIfAbsent(edge.getNode1(), n -> new HashMap<>()) .computeIfAbsent(edge.getInt1(), i -> AclIpSpace.builder()) .thenPermitting((_arpReplies.get(edge.getNode2()).get(edge.getInt2())))); return someoneRepliesByNode.entrySet().stream() .collect( ImmutableMap.toImmutableMap( Entry::getKey /* hostname */, someoneRepliesByNodeEntry -> someoneRepliesByNodeEntry.getValue().entrySet().stream() .collect( ImmutableMap.toImmutableMap( Entry::getKey /* interface */, someoneRepliesByInterfaceEntry -> someoneRepliesByInterfaceEntry.getValue().build())))); } }
@Override public IpSpace visitAclIpSpace(AclIpSpace aclIpSpace) { AclIpSpace.Builder renamedSpace = AclIpSpace.builder(); aclIpSpace .getLines() .forEach( line -> { IpSpace space = line.getIpSpace().accept(this); renamedSpace.thenAction(line.getAction(), space); }); return renamedSpace.build(); }
/** Convert address book into corresponding IpSpaces */ private Map<String, IpSpace> toIpSpaces(String bookName, AddressBook book) { Map<String, IpSpace> ipSpaces = new TreeMap<>(); book.getEntries() .forEach( (n, entry) -> { String entryName = bookName + "~" + n; // If this address book references other entries, add them to an AclIpSpace if (!entry.getEntries().isEmpty()) { AclIpSpace.Builder aclIpSpaceBuilder = AclIpSpace.builder(); entry .getEntries() .keySet() .forEach( name -> { String subEntryName = bookName + "~" + name; aclIpSpaceBuilder.thenPermitting(new IpSpaceReference(subEntryName)); }); ipSpaces.put(entryName, aclIpSpaceBuilder.build()); } else { ipSpaces.put( entryName, IpWildcardSetIpSpace.builder().including(entry.getIpWildcards(_w)).build()); } }); return ipSpaces; }
/** * @param aclIpSpace The {@link AclIpSpace} to dereference * @return An {@link AclIpSpace} identical to the original but with all uses of {@link * IpSpaceReference} replaced with the dereferenced {@link IpSpace} they represent * @throws CircularReferenceException if original {@link AclIpSpace} points to a cyclical * reference. * @throws UndefinedReferenceException if original {@link AclIpSpace} points to an undefined * reference. */ @Override public IpSpace visitAclIpSpace(AclIpSpace aclIpSpace) throws CircularReferenceException, UndefinedReferenceException { AclIpSpace.Builder sanitizedSpace = AclIpSpace.builder(); for (AclIpSpaceLine line : aclIpSpace.getLines()) { IpSpace ipSpace = line.getIpSpace().accept(this); sanitizedSpace.thenAction(line.getAction(), ipSpace); } // No cycles/undefined references in this AclIpSpace. Return reference-free version. return sanitizedSpace.build(); }
@Test public void testAclIpSpace_simplifyOneAccept() { assertThat( SIMPLIFIER.simplify(AclIpSpace.builder().thenPermitting(IP1234.toIpSpace()).build()), equalTo(IP1234.toIpSpace())); }
@Test public void testVisitAclIpSpace_simplifyLines() { // simplification should simplify the IpSpaces of each line assertThat( SIMPLIFIER.simplify( AclIpSpace.builder() .thenRejecting(IP1234.toIpSpace()) .thenPermitting(IpWildcard.ANY.toIpSpace()) .build()), equalTo( AclIpSpace.builder() .thenRejecting(IP1234.toIpSpace()) .thenPermitting(UniverseIpSpace.INSTANCE) .build())); }
/** * Returns an {@link IpSpace} that contains this prefix except for the network and broadcast * addresses, if applicable. Following RFC 3021, the entire {@code /31} is treated as host IP * space. A prefix of length {@code /32} is preserved, though may be a degenerate case. */ public IpSpace toHostIpSpace() { if (_prefixLength >= Prefix.MAX_PREFIX_LENGTH - 1) { return toIpSpace(); } return AclIpSpace.builder() .thenRejecting(getStartIp().toIpSpace()) .thenRejecting(getEndIp().toIpSpace()) .thenPermitting(toIpSpace()) .build(); }
@Test public void testVisitAclIpSpace_removeLinesAfterUniverse() { // simplification should remove any line after a UniverseIpSpace assertThat( SIMPLIFIER.simplify( AclIpSpace.builder() .thenRejecting(IP1234.toIpSpace()) .thenPermitting(UniverseIpSpace.INSTANCE) .thenRejecting(IP1234.toIpSpace()) .build()), equalTo( AclIpSpace.builder() .thenRejecting(IP1234.toIpSpace()) .thenPermitting(UniverseIpSpace.INSTANCE) .build())); }
@Test public void testVisitAclIpSpace_removeEmptyLines() { // simplification should remove EmptyIpSpace lines assertThat( SIMPLIFIER.simplify( AclIpSpace.builder() .thenRejecting(EmptyIpSpace.INSTANCE) .thenPermitting(IP1234.toIpSpace()) .thenPermitting(IP1234.toIpSpace()) .build()), equalTo( AclIpSpace.builder() .thenPermitting(IP1234.toIpSpace()) .thenPermitting(IP1234.toIpSpace()) .build())); }
@Test public void testVisitAclIpSpace_toUniverse() { /* * If after simplification all lines are permits and the last is universe, * then simplify the entire AclIpSpace to UniverseIpSpace. */ assertThat( SIMPLIFIER.simplify( AclIpSpace.builder() .thenPermitting(IP1234.toIpSpace()) .thenPermitting(IpWildcard.ANY.toIpSpace()) .thenRejecting(IP1234.toIpSpace()) .build()), equalTo(UniverseIpSpace.INSTANCE)); }
@Test public void testAclIpSpace() { IpSpaceDereferencer dereferencer = new IpSpaceDereferencer(NAMED_IP_SPACES); AclIpSpace input = AclIpSpace.builder() .thenPermitting(new IpSpaceReference("empty")) .thenRejecting(new IpSpaceReference("prefix")) .thenPermitting(new IpSpaceReference("namedIp")) .thenRejecting(UniverseIpSpace.INSTANCE) .build(); AclIpSpace expected = AclIpSpace.builder() .thenPermitting(NAMED_IP_SPACES.get("empty")) .thenRejecting(NAMED_IP_SPACES.get("prefix")) .thenPermitting(NAMED_IP_SPACES.get("ip")) .thenRejecting(UniverseIpSpace.INSTANCE) .build(); assertThat(dereferencer.visitAclIpSpace(input), equalTo(expected)); } }
@Test public void testStopWhenEmpty() { AclIpSpace space = AclIpSpace.builder() .thenPermitting(Prefix.parse("1.2.3.4/32").toIpSpace()) .thenRejecting(UniverseIpSpace.INSTANCE) .thenPermitting(Prefix.parse("2.0.0.0/8").toIpSpace()) .build(); AclIpSpace expected = AclIpSpace.builder() .thenPermitting(Prefix.parse("1.2.3.4/32").toIpSpace()) .thenRejecting(UniverseIpSpace.INSTANCE) .build(); assertThat(space, equalTo(expected)); } }