Only DHCP peers from a local network are configured in ACLs.
Deltas are computed to update specific rules to avoid rewriting
entire ACLs.
Change-Id: I9245c9ab961a1090d1d0d04ead937b78f789f3bf
Signed-off-by: Tomas Cechvala <tcechval@cisco.com>
import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
import org.opendaylight.vbd.impl.transaction.VbdNetconfTransaction;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.LocationType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.eventbus.Subscribe;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
public class InterfaceManager implements AutoCloseable {
LOG.warn("Interface was not added to bridge-domain {} for endpoint {}", l2FloodDomain, rEp, e);
}
}
- aclManager.updateAclsForPeers(policyCtx, rEpKey);
} else {
LOG.debug("Forwarding is not created - Location of renderer endpoint contains "
+ "external-node therefore VPP renderer assumes that interface for endpoint is "
import org.opendaylight.groupbasedpolicy.renderer.vpp.config.ConfigUtil;
import org.opendaylight.groupbasedpolicy.renderer.vpp.event.NodeOperEvent;
import org.opendaylight.groupbasedpolicy.renderer.vpp.event.RendererPolicyConfEvent;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl.AccessListWrapper;
import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl.AclManager;
import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory;
+import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory;
import org.opendaylight.groupbasedpolicy.util.IidFactory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.RendererPolicy;
ImmutableSet<RendererEndpointKey> rendEpsAfter = policyCtxAfter.getPolicyTable().rowKeySet();
SetView<RendererEndpointKey> removedRendEps = Sets.difference(rendEpsBefore, rendEpsAfter);
- removedRendEps.forEach(rEpKey -> fwManager.removeForwardingForEndpoint(rEpKey, policyCtxBefore));
+ removedRendEps.forEach(rEpKey -> fwManager.removeForwardingForEndpoint(rEpKey, policyCtxBefore));//TODO
if (!ConfigUtil.getInstance().isL3FlatEnabled()) {
LOG.debug("Removing bridge domains on nodes {}", removedVppNodesByL2Fd);
fwManager.syncRouting(policyCtxAfter);
SetView<RendererEndpointKey> createdRendEps = Sets.difference(rendEpsAfter, rendEpsBefore);
- createdRendEps.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtxAfter));
+ createdRendEps.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtxAfter));//TODO
SetView<RendererEndpointKey> updatedRendEps = Sets.intersection(rendEpsBefore, rendEpsAfter);
// update forwarding for endpoint
rEpKeys.forEach(rEpKey -> fwManager.createForwardingForEndpoint(rEpKey, policyCtx));
fwManager.createNatEntries(policyCtx);
fwManager.syncRouting(policyCtx);
+
+ ImmutableSet<RendererEndpointKey> rendEpsAfter = policyCtx.getPolicyTable().rowKeySet();
+ ImmutableSet<RuleGroupKey> rulesAfter = policyCtx.getRuleGroupByKey().keySet();
+ LOG.info("rendererPolicyCreated Created rules {}", rulesAfter);
+ LOG.info("rendererPolicyCreated Created renderer endpoints {}", rendEpsAfter);
+ aclManager.resolveRulesToConfigure(policyCtx, Sets.difference(rendEpsAfter, Sets.newHashSet()),
+ Sets.difference(rulesAfter, Sets.newHashSet()), true);
+
}
private void rendererPolicyDeleted(RendererPolicy rendererPolicy) {
}
fwManager.deleteNatEntries(policyCtx);
fwManager.deleteRouting(policyCtx);
+
+ ImmutableSet<RendererEndpointKey> rendEpsBefore = policyCtx.getPolicyTable().rowKeySet();
+ ImmutableSet<RuleGroupKey> rulesBefore = policyCtx.getRuleGroupByKey().keySet();
+ LOG.debug("rendererPolicyDeleted. Removed rules {}", rulesBefore);
+ LOG.debug("rendererPolicyDeleted. Endpoints deleted {}", rendEpsBefore);
+ aclManager.resolveRulesToConfigure(policyCtx, Sets.difference(rendEpsBefore, Sets.newHashSet()),
+ Sets.difference(rulesBefore, Sets.newHashSet()), false);
+
}
private static SetMultimap<String, NodeId> resolveVppNodesByL2Fd(Set<RendererEndpointKey> rEpKeys,
.stream()
.filter(peerEpKey -> peerHasLocation(ctx, peerEpKey))
.forEach(peerEpKey -> {
+ List<GbpAceBuilder> rules = new ArrayList<>();
ctx.getPolicyTable().get(rEpKey, peerEpKey).forEach(resolvedRules -> {
- List<GbpAceBuilder> rules = new ArrayList<>();
Direction classifDir = calculateClassifDirection(resolvedRules.getRendererEndpointParticipation(),
policyDirection);
rules.addAll(resolveAclRulesFromPolicy(resolvedRules, classifDir, rEpKey, peerEpKey));
- updateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true);
- aclWrapper.writeRules(rules);
});
+ if (validateAndUpdateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true)) {
+ aclWrapper.writeRules(rules);
+ }
});
}
return Direction.In;
}
- static void updateAddressesInRules(List<GbpAceBuilder> rules, RendererEndpointKey rEpKey, PeerEndpointKey peerEpKey,
- PolicyContext ctx, ACE_DIRECTION policyDirection, boolean resolveForLocationPeers) {
+ static boolean validateAndUpdateAddressesInRules(List<GbpAceBuilder> rules, RendererEndpointKey rEpKey,
+ PeerEndpointKey peerEpKey, PolicyContext ctx, ACE_DIRECTION policyDirection,
+ boolean resolveForLocationPeers) {
for (AddressMapper addrMapper : Arrays.asList(new SourceMapper(policyDirection),
new DestinationMapper(policyDirection))) {
if (peerHasLocation(ctx, peerEpKey) && resolveForLocationPeers) {
- addrMapper.updateRules(rules, findAddrEp(ctx, rEpKey), findAddrEp(ctx, peerEpKey));
+ if (!addrMapper.updateRules(rules, findAddrEp(ctx, rEpKey), findAddrEp(ctx, peerEpKey))) {
+ return false;
+ }
} else if (!peerHasLocation(ctx, peerEpKey) && !resolveForLocationPeers) {
addrMapper.updateExtRules(rules, findAddrEp(ctx, rEpKey), null);
}
}
+ return true;
}
private static boolean peerHasLocation(PolicyContext ctx, PeerEndpointKey peerEpKey) {
}
static void setSourceL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
- InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
- if (addr instanceof Inet6Address) {
+ if (isIpv6Address(address)) {
rule.setIpAddresses(new Ipv6Prefix(address), null);
} else {
rule.setIpAddresses(new Ipv4Prefix(address), null);
}
static void setDestinationL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
- InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
- if (addr instanceof Inet6Address) {
+ if (isIpv6Address(address)) {
rule.setIpAddresses(null, new Ipv6Prefix(address));
} else {
rule.setIpAddresses(null, new Ipv4Prefix(address));
}
}
+ public static boolean isIpv4Address(String address) throws UnknownHostException {
+ InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
+ return addr instanceof Inet4Address;
+ }
+
+ public static boolean isIpv6Address(String address) throws UnknownHostException {
+ InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
+ return addr instanceof Inet6Address;
+ }
+
static GbpAceBuilder denyIngressTrafficForPrefix(Subnet subnet) {
IpPrefix ipPrefix = subnet.getIpPrefix();
if (ipPrefix.getIpv4Prefix() != null) {
};
beans.forEach(bean -> {
List<GbpAceBuilder> aceBuilders = bean.resolveAces(resolveUpdates(policyCtx, bean.epKey, bean.ruleGroups));
- getInterfacesForEndpoint(policyCtx, KeyFactory.addressEndpointKey(bean.epKey)).asMap()
+ resolveInterfaces(KeyFactory.addressEndpointKey(bean.epKey), policyCtx).asMap()
.forEach((nodeId, intfcs) -> {
- intfcs.stream()
- .filter(intf -> !interfaceManager.isExcludedFromPolicy(nodeId, getIntfName.apply(intf)))
- .forEach(intf -> {
- AclKey aclKey = new AclKey(getIntfName.apply(intf) + bean.aceDirection, VppAcl.class);
- denyTenantTraffic.put(nodeId, aclKey,
- getWorkaroundFlows(policyCtx, bean.epKey, bean.aceDirection));
- AccessListUtil.allowExternalNetworksForEp(bean.epKey, bean.aceDirection).build();
- denyTenantTraffic.put(nodeId, aclKey,
- AccessListUtil.denyDomainSubnets(policyCtx, bean.aceDirection)
- .stream()
- .map(GbpAceBuilder::build)
- .collect(Collectors.toList()));
- permitExternal.put(nodeId, aclKey, Lists.newArrayList(
- AccessListUtil.allowExternalNetworksForEp(bean.epKey, bean.aceDirection).build()));
- Optional<List<Ace>> entries = Optional.fromNullable(aceTable.get(nodeId, aclKey));
- aceTable.put(nodeId, aclKey,
- Stream
- .concat((entries.isPresent()) ? entries.get().stream() : Stream.empty(),
- aceBuilders.stream().map(aceBuilder -> aceBuilder.build()))
- .collect(Collectors.toList()));
- });
+ intfcs.stream().forEach(intf -> {
+ if (!write && changedEndpoints.contains(bean.epKey)) {
+ AccessListWrapper.removeAclsForInterface(VppIidFactory.getNetconfNodeIid(nodeId), intf);
+ return;
+ }
+ AclKey aclKey = new AclKey(getIntfName.apply(intf) + bean.aceDirection, VppAcl.class);
+ denyTenantTraffic.put(nodeId, aclKey,
+ getWorkaroundFlows(policyCtx, bean.epKey, bean.aceDirection));
+ AccessListUtil.allowExternalNetworksForEp(bean.epKey, bean.aceDirection).build();
+ denyTenantTraffic.put(nodeId, aclKey,
+ AccessListUtil.denyDomainSubnets(policyCtx, bean.aceDirection)
+ .stream()
+ .map(GbpAceBuilder::build)
+ .collect(Collectors.toList()));
+ permitExternal.put(nodeId, aclKey, Lists.newArrayList(
+ AccessListUtil.allowExternalNetworksForEp(bean.epKey, bean.aceDirection).build()));
+ Optional<List<Ace>> entries = Optional.fromNullable(aceTable.get(nodeId, aclKey));
+ aceTable.put(nodeId, aclKey,
+ Stream
+ .concat((entries.isPresent()) ? entries.get().stream() : Stream.empty(),
+ aceBuilders.stream().map(aceBuilder -> aceBuilder.build()))
+ .collect(Collectors.toList()));
+ });
});
});
- // to avoid empty ACL (IllegalStateArgument on HC), rules have to be updated gently
updateRules(ImmutableTable.copyOf(aceTable), write);
- updateRules(ImmutableTable.copyOf(denyTenantTraffic), false);
- updateRules(ImmutableTable.copyOf(denyTenantTraffic), true);
- updateRules(ImmutableTable.copyOf(permitExternal), false);
- updateRules(ImmutableTable.copyOf(permitExternal), true);
+ if (write) {
+ // to avoid empty ACL (IllegalStateArgument on HC), rules have to be updated gently
+ updateRules(ImmutableTable.copyOf(denyTenantTraffic), false);
+ updateRules(ImmutableTable.copyOf(denyTenantTraffic), true);
+ updateRules(ImmutableTable.copyOf(permitExternal), false);
+ updateRules(ImmutableTable.copyOf(permitExternal), true);
+ }
}
+ public ImmutableSetMultimap<NodeId, InterfaceKey> resolveInterfaces(AddressEndpointKey key,
+ PolicyContext policyCtx) {
+ Function<InterfaceKey, String> getIntfName = (intf) -> {
+ Optional<String> intfName = VppPathMapper.interfacePathToInterfaceName(intf.getName());
+ Preconditions.checkArgument(intfName.isPresent(), "Failed to resolve interface name from " + intf);
+ return intfName.get();
+ };
+ SetMultimap<NodeId, InterfaceKey> result = HashMultimap.create();
+ getInterfacesForEndpoint(policyCtx, key).forEach((nodeId, intf) -> {
+ if (!interfaceManager.isExcludedFromPolicy(nodeId, getIntfName.apply(intf))) {
+ result.put(nodeId, intf);
+ }
+ });
+ return ImmutableSetMultimap.copyOf(result);
+ }
+
private List<Ace> getWorkaroundFlows(PolicyContext policyCtx, RendererEndpointKey key, ACE_DIRECTION dir) {
List<Ace> result = new ArrayList<>();
result.addAll(AccessListUtil.denyDomainSubnets(policyCtx, dir)
rendEp.get()
.getPeerEndpoint()
.stream()
- // TODO see UT
.filter(peerEp -> policyCtx.getPolicyTable().get(rendEpKey, peerEp.getKey()) != null)
+ .filter(peerEp -> !endpointIsExcludedFromPolicy(policyCtx, KeyFactory.addressEndpointKey(peerEp.getKey())))
.forEach(peerEp -> {
updateTreeBuilder.put(rendEpKey, peerEp.getKey(),
policyCtx.getPolicyTable()
return updateTreeBuilder.build();
}
+ private boolean endpointIsExcludedFromPolicy(PolicyContext policyCtx, AddressEndpointKey key) {
+ return getInterfacesForEndpoint(policyCtx, key).entries().stream().anyMatch(entry -> {
+ Optional<String> intfName = VppPathMapper.interfacePathToInterfaceName(entry.getValue().getName());
+ return intfName.isPresent() ? interfaceManager.isExcludedFromPolicy(entry.getKey(), intfName.get()) : false;
+ });
+ }
+
private void updateRules(ImmutableTable<NodeId, AclKey, List<Ace>> rulesToUpdate, boolean write) {
List<ListenableFuture<Void>> sync = new ArrayList<>();
rulesToUpdate.rowKeySet().forEach(nodeId -> {
@Override
public Void call() throws Exception {
InstanceIdentifier<Node> vppIid = VppIidFactory.getNetconfNodeIid(nodeId);
- Optional<DataBroker> dataBroker =
- mountDataProvider.resolveDataBrokerForMountPoint(vppIid);
+ Optional<DataBroker> dataBroker = mountDataProvider.resolveDataBrokerForMountPoint(vppIid);
if (!dataBroker.isPresent()) {
LOG.error("Failed to update ACLs for endpoints on node {}. Mount point does not exist.",
nodeId);
.child(Ace.class, ace.getKey())
.build(), ace);
});
+ LOG.debug("Writing ACE changes - Node: {} Entries: {}", nodeId, entries);
if (entries.isEmpty()) {
return;
}
}
private List<GbpAceBuilder> generateRulesForEndpointPair(PolicyContext ctx, RendererEndpointKey r,
- PeerEndpointKey p, RendererResolvedPolicy rrp, ACE_DIRECTION aceDirection) {
+ PeerEndpointKey p, List<RendererResolvedPolicy> rrps, ACE_DIRECTION aceDirection) {
List<GbpAceBuilder> rules = new ArrayList<>();
- Direction direction =
- AccessListUtil.calculateClassifDirection(rrp.getRendererEndpointParticipation(), aceDirection);
- ResolvedRuleGroup resolvedRuleGroup = ctx.getRuleGroupByKey().get(rrp.getRuleGroup().getRelatedRuleGroupKey());
- resolvedRuleGroup.getRules().forEach(rule -> {
- Optional<GbpAceBuilder> ace = AccessListUtil.resolveAceClassifersAndAction(rule, direction,
- AccessListUtil.resolveAceName(rule.getName(), r, p));
- if (ace.isPresent()) {
- rules.add(ace.get());
- }
- });
- AccessListUtil.updateAddressesInRules(rules, r, p, ctx, aceDirection, true);
- return rules;
- }
-
- // TODO remove
- public void updateAclsForPeers(PolicyContext policyCtx, RendererEndpointKey rEpKey) {
- ImmutableSet<PeerEndpointKey> peers = policyCtx.getPolicyTable().row(rEpKey).keySet();
- List<ListenableFuture<Void>> sync = new ArrayList<>();
- for (RendererEndpointKey peerRendEp : peers.stream()
- .map(AddressEndpointUtils::fromPeerEpKey)
- .collect(Collectors.toList())
- .stream()
- .map(AddressEndpointUtils::toRendererEpKey)
- .collect(Collectors.toList())) {
- sync.add(updateAclsForRendEp(peerRendEp, policyCtx));
- }
- try {
- Futures.allAsList(sync).get();
- } catch (InterruptedException | ExecutionException e) {
- LOG.error("Failed to update ACLs for peers of {}. {}", rEpKey, e);
+ for (RendererResolvedPolicy rrp : rrps) {
+ Direction direction =
+ AccessListUtil.calculateClassifDirection(rrp.getRendererEndpointParticipation(), aceDirection);
+ ResolvedRuleGroup resolvedRuleGroup =
+ ctx.getRuleGroupByKey().get(rrp.getRuleGroup().getRelatedRuleGroupKey());
+ resolvedRuleGroup.getRules().forEach(rule -> {
+ Optional<GbpAceBuilder> ace = AccessListUtil.resolveAceClassifersAndAction(rule, direction,
+ AccessListUtil.resolveAceName(rule.getName(), r, p));
+ if (ace.isPresent()) {
+ rules.add(ace.get());
+ }
+ });
}
+ return rules;
}
public ListenableFuture<Void> updateAclsForRendEp(RendererEndpointKey rEpKey, PolicyContext policyCtx) {
List<GbpAceBuilder> resolveAces(
ImmutableTable<RendererEndpointKey, PeerEndpointKey, List<RendererResolvedPolicy>> updateTree) {
+ return (write) ? resolveCreatedAces(updateTree) : resolveRemovedAces(updateTree);
+ }
+
+ List<GbpAceBuilder> resolveCreatedAces(
+ ImmutableTable<RendererEndpointKey, PeerEndpointKey, List<RendererResolvedPolicy>> updateTree) {
List<GbpAceBuilder> result = new ArrayList<>();
updateTree.columnKeySet().stream().filter(peer -> updateTree.get(epKey, peer) != null).forEach(peer -> {
+ List<GbpAceBuilder> deltaRules =
+ generateRulesForEndpointPair(policyCtx, epKey, peer, updateTree.get(epKey, peer), aceDirection);
+ if (AccessListUtil.validateAndUpdateAddressesInRules(deltaRules, epKey, peer, policyCtx, aceDirection,
+ true)) {
+ result.addAll(deltaRules);
+ }
+ });
+ return result;
+ }
+
+ List<GbpAceBuilder> resolveRemovedAces(
+ ImmutableTable<RendererEndpointKey, PeerEndpointKey, List<RendererResolvedPolicy>> updateTree) {
+ List<GbpAceBuilder> result = new ArrayList<>();
+ updateTree.columnKeySet().stream().filter(peer -> updateTree.get(epKey, peer) != null).forEach(peer -> {
+ // we only need to resolve rule names when removing ACE
updateTree.get(epKey, peer).stream().forEach(rrp -> {
- if (write) {
- result.addAll(generateRulesForEndpointPair(policyCtx, epKey, peer, rrp, aceDirection));
- } else {
- // we only need to resolve rule names when removing ACE
- result.addAll(rrp.getRuleGroup()
- .getRules()
- .stream()
- .map(rule -> new GbpAceBuilder(AccessListUtil.resolveAceName(rule.getName(), epKey, peer)))
- .collect(Collectors.toList()));
- }
+ result.addAll(rrp.getRuleGroup()
+ .getRules()
+ .stream()
+ .map(rule -> new GbpAceBuilder(AccessListUtil.resolveAceName(rule.getName(), epKey, peer)))
+ .collect(Collectors.toList()));
});
});
return result;
package org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRangeBuilder;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRangeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.containment.endpoints.ContainmentEndpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.IpPrefixType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.L2BridgeDomain;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.L3Context;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.MacAddressType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.vpp.ace.vpp.ace.nodes.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.access.lists.acl.access.list.entries.ace.matches.ace.type.vpp.ace.vpp.ace.nodes.ace.ip.version.AceIpv6;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
abstract class AddressMapper {
+ private static final Logger LOG = LoggerFactory.getLogger(AddressMapper.class);
+
private AccessListUtil.ACE_DIRECTION direction;
private static final PortNumber DHCP_67 = new PortNumber(67);
private static final PortNumber DHCP_68 = new PortNumber(68);
private static final PortNumber DHCPV6_547 = new PortNumber(547);
private static final PortNumber DHCPV6_548 = new PortNumber(548);
+ private static final ImmutableMap<PortNumber, PortNumber> dhcpSockets =
+ ImmutableMap.of(DHCP_67, DHCP_68, DHCP_68, DHCP_67, DHCPV6_548, DHCPV6_547, DHCPV6_547, DHCPV6_548);
AddressMapper(AccessListUtil.ACE_DIRECTION direction) {
this.direction = direction;
abstract List<GbpAceBuilder> updateExtRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
ContainmentEndpoint cEp);
- public List<GbpAceBuilder> updateRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
+ public boolean updateRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
AddressEndpointWithLocation peerEp) {
- if (this instanceof SourceMapper) {
- if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
- return updateRules(rules, localEp);
+ filterRulesWithIrrelevantAddresses(rules, localEp, peerEp);
+ for (GbpAceBuilder rule : rules) {
+ LOG.info("Rule: {} Updating rule between {} and {}. Start.", rule, localEp.getAddress(),
+ peerEp.getAddress());
+ boolean isDhcpRule = dhcpSockets.entrySet().stream().anyMatch(dhcpSocket -> {
+ boolean srcMatch = isInRange(rule.getSourcePortRangeBuilder(), dhcpSocket.getKey());
+ boolean dstMatch = isInRange(rule.getDestinationPortRangeBuilder(), dhcpSocket.getValue());
+ return srcMatch && dstMatch;
+ });
+ if (isDhcpRule) {
+ if (!inSameSubnet(localEp, peerEp)) {
+ // do not process rules for DHCPs of other networks
+ LOG.info("Rule: {} Not updating rules between {} and {}. Returning false.", rule,
+ localEp.getAddress(), peerEp.getAddress());
+ return false;
+ }
+ // do not update addresses for DHCP traffic
+ LOG.info("Rule: {} Not updating rule between {} and {}. Continue with next.", rule,
+ localEp.getAddress(), peerEp.getAddress());
+ continue;
}
- return updateRules(rules, peerEp);
- }
- if (this instanceof DestinationMapper) {
- if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
- return updateRules(rules, peerEp);
+ if (this instanceof SourceMapper) {
+ LOG.info("Rule: {} Updating rule between {} and {}. SourceMapper.", rule, localEp.getAddress(),
+ peerEp.getAddress());
+ if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
+ updateRule(localEp, rule);
+ continue;
+ }
+ updateRule(peerEp, rule);
+ } else if (this instanceof DestinationMapper) {
+ LOG.info("Rule: {} Updating rule between {} and {}. SourceMapper.", rule, localEp.getAddress(),
+ peerEp.getAddress());
+ if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
+ updateRule(peerEp, rule);
+ continue;
+ }
+ updateRule(localEp, rule);
}
- return updateRules(rules, localEp);
}
- return rules;
+ LOG.info("Updating rules between {} and {}. Done, returning true.",
+ localEp.getAddress(), peerEp.getAddress());
+ return true;
}
- private List<GbpAceBuilder> updateRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation addrEp) {
- for (GbpAceBuilder rule : rules) {
- if (isInRange(rule.getSourcePortRangeBuilder(), DHCP_67)
- && isInRange(rule.getDestinationPortRangeBuilder(), DHCP_68)) {
- continue;
- }
- if (isInRange(rule.getSourcePortRangeBuilder(), DHCP_68)
- && isInRange(rule.getDestinationPortRangeBuilder(), DHCP_67)) {
- continue;
- }
- if (isInRange(rule.getSourcePortRangeBuilder(), DHCPV6_547)
- && isInRange(rule.getDestinationPortRangeBuilder(), DHCPV6_548)) {
- continue;
+ private void filterRulesWithIrrelevantAddresses(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
+ AddressEndpointWithLocation peerEp) {
+ try {
+ if (!IpPrefixType.class.equals(localEp.getAddressType())
+ || !IpPrefixType.class.equals(peerEp.getAddressType())) {
+ return;
}
- if (isInRange(rule.getSourcePortRangeBuilder(), DHCPV6_548)
- && isInRange(rule.getDestinationPortRangeBuilder(), DHCPV6_547)) {
- continue;
+ boolean addressV4Check = AccessListUtil.isIpv4Address(localEp.getAddress())
+ && AccessListUtil.isIpv4Address(peerEp.getAddress());
+ boolean addressV6Check = AccessListUtil.isIpv6Address(localEp.getAddress())
+ && AccessListUtil.isIpv6Address(peerEp.getAddress());
+ Predicate<GbpAceBuilder> p;
+ if (addressV4Check) {
+ p = rule -> rule.getEtherType().get() instanceof AceIpv6;
+ } else if (addressV6Check) {
+ p = rule -> rule.getEtherType().get() instanceof AceIpv4;
+ } else {
+ return;
}
- updateRule(addrEp, rule);
+ List<GbpAceBuilder> rulesToRemove = rules.stream()
+ .filter(rule -> rule.getEtherType().isPresent())
+ .filter(p)
+ .collect(Collectors.toList());
+ rulesToRemove.forEach(rule -> { LOG.info("Filtering rules by ethertype {}", rule); rules.remove(rule);});
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to parse addresses {}", e);
+ }
+ }
+
+ private static boolean inSameSubnet(AddressEndpointWithLocation localEp, AddressEndpointWithLocation peerEp) {
+ List<Predicate<AddressEndpointWithLocation>> list = new ArrayList<>();
+ list.add(x -> x != null);
+ list.add(x -> x.getContextType().equals(L3Context.class));
+ list.add(x -> x.getChildEndpoint() != null && !x.getChildEndpoint().isEmpty());
+ list.add(x -> x.getAbsoluteLocation() != null);
+ list.add(x -> x.getChildEndpoint().get(0).getContextType().equals(L2BridgeDomain.class));
+ list.add(x -> x.getChildEndpoint().get(0).getAddressType().equals(MacAddressType.class));
+ if (list.stream().filter(i -> !i.apply(peerEp)).findFirst().isPresent()) {
+ return false;
}
- return rules;
+ return localEp.getChildEndpoint().get(0).getContextId().equals(peerEp.getChildEndpoint().get(0).getContextId());
}
- private boolean isInRange(SourcePortRangeBuilder spr, PortNumber portNumber) {
+ private static boolean isInRange(SourcePortRangeBuilder spr, PortNumber portNumber) {
return spr != null && isInRange(spr.getLowerPort(), spr.getUpperPort(), portNumber);
}
- private boolean isInRange(DestinationPortRangeBuilder dpr, PortNumber portNumber) {
+ private static boolean isInRange(DestinationPortRangeBuilder dpr, PortNumber portNumber) {
return dpr != null && isInRange(dpr.getLowerPort(),dpr.getUpperPort(), portNumber);
}
- private boolean isInRange(PortNumber lower, PortNumber upper, PortNumber ref) {
+ private static boolean isInRange(PortNumber lower, PortNumber upper, PortNumber ref) {
if (lower != null && upper != null) {
return (lower.getValue() <= ref.getValue()) && (ref.getValue() <= upper.getValue());
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.acl.ip.protocol.header.fields.ip.protocol.udp.UdpNodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev170615.acl.ip.protocol.header.fields.ip.protocol.udp.UdpNodesBuilder;
+import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
public class GbpAceBuilder {
return action;
}
+ public Optional<AceIpVersion> getEtherType() {
+ return (aceIpVersion != null) ? Optional.of(aceIpVersion) : Optional.absent();
+ }
+
public GbpAceBuilder setProtocol(short protocol) {
this.protocol = protocol;
return this;
return this;
}
+ public GbpAceBuilder setIpv4EtherType() {
+ this.aceIpVersion = new AceIpv4Builder().build();
+ return this;
+ }
+
+ public GbpAceBuilder setIpv6EtherType() {
+ this.aceIpVersion = new AceIpv6Builder().build();
+ return this;
+ }
+
public GbpAceBuilder setIpAddresses(@Nullable Ipv4Prefix srcIp, @Nullable Ipv4Prefix dstIp) {
AceIpv4Builder aceIpv4Builder = (aceIpv4 != null) ? new AceIpv4Builder(aceIpv4) : new AceIpv4Builder();
if (srcIp != null) {
@Override
GbpAceBuilder update(GbpAceBuilder ruleBuilder, Map<String, ParameterValue> params) {
- // ETHER_TYPE matching not supported yet
+ Long etherType = getEtherTypeValue(params);
+ if (!ruleBuilder.getEtherType().isPresent() && etherType != null) {
+ if (EtherTypeClassifierDefinition.IPv4_VALUE.equals(etherType)) {
+ ruleBuilder.setIpv4EtherType();
+ } else if (EtherTypeClassifierDefinition.IPv6_VALUE.equals(etherType)) {
+ ruleBuilder.setIpv6EtherType();
+ }
+ }
return ruleBuilder;
}
+ private static Long getEtherTypeValue(Map<String, ParameterValue> params) {
+ if (params == null || params.get(EtherTypeClassifierDefinition.ETHERTYPE_PARAM) == null) {
+ return null;
+ }
+ return params.get(EtherTypeClassifierDefinition.ETHERTYPE_PARAM).getIntValue();
+ }
+
@Override
void checkPrereqs(GbpAceBuilder matchBuilders) {
// Nothing to consider yet.
fromKey.getContextType());
}
+ public static AddressEndpointKey addressEndpointKey(PeerEndpointKey fromKey) {
+ return new AddressEndpointKey(fromKey.getAddress(), fromKey.getAddressType(), fromKey.getContextId(),
+ fromKey.getContextType());
+ }
+
public static ProviderAddressEndpointLocationKey providerAddressEndpointLocationKey(
AddressEndpointWithLocationKey fromKey) {
return new ProviderAddressEndpointLocationKey(fromKey.getAddress(), fromKey.getAddressType(),
import org.opendaylight.vbd.impl.transaction.VbdNetconfTransaction;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AccessLists;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ContextId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
@Test
public void oneEndpointChanged() {
List<Acl> acls = processChangedEndpoints(Sets.newHashSet(rendererEndpointKey(l3AddrEp1.getKey())), true);
- Assert.assertEquals(acls.size(), 6);
+ Assert.assertEquals(6, acls.size());
acls.forEach(acl -> {
if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
Assert.assertEquals(2 + 4, acl.getAccessListEntries().getAce().size());
}
});
acls = processChangedEndpoints(Sets.newHashSet(rendererEndpointKey(l3AddrEp1.getKey())), false);
- Assert.assertEquals(acls.size(), 6);
+ Assert.assertEquals(6, acls.size());
acls.forEach(acl -> {
+ System.out.println("DEBUGX " + acl);
if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
- Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
+ acl.getAccessListEntries().getAce().forEach(ace -> {
+ System.out.println("DEBUG1 " + ace);
+ });
+ /*
+ * TODO rules were not removed, entire ACL is removed elsewhere when interface at
+ * NODE1_CONNECTOR_1 detaches from bridge domain.
+ */
+ Assert.assertEquals(6, acl.getAccessListEntries().getAce().size());
} else {
Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
}
Assert.assertEquals(acls.size(), 4);
acls.forEach(acl -> {
if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
- // TODO fix so that the following assert is uncomemnted
- // Assert.assertEquals(2 + 2, acl.getAccessListEntries().getAce().size());
+ Assert.assertEquals(2 + 4, acl.getAccessListEntries().getAce().size());
} else {
Assert.assertEquals(2 + 2, acl.getAccessListEntries().getAce().size());
}
Assert.assertEquals(acls.size(), 4);
acls.forEach(acl -> {
if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
+ // Peer is updated 4 rules went to 2.
Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
} else {
+ // Entire ACL is removed elsewhere -> no update of rules needed.
+ Assert.assertEquals(4, acl.getAccessListEntries().getAce().size());
+ }
+ });
+ }
+
+ /**
+ * Tests DHCP rules configuration. There should be no entries for DHCP servers serving different
+ * subnets, i.e.
+ * hosts should only have rules for accessing DHCP server in their subnet.
+ */
+ @Test
+ public void dhcpDifferentSubnets() {
+ final String ep4Mac = "20:00:00:00:00:02";
+ final String ep4Ip = "20.0.0.2/32";
+ final ContextId l2BdId2 = new ContextId("l2BridgeDomainId2");
+ final String node1Connector4 =
+ "/ietf-interfaces:interfaces/ietf-interfaces:interface[ietf-interfaces:name='nodeConnector4']";
+ final AddressEndpointWithLocation l2DhcpEp = l2AddressEndpointWithLocation(ep4Mac, l2BdId2, ep4Ip, L3_CTX_ID);
+ final AddressEndpointWithLocation l3DhcpEp = appendLocationToEndpoint(
+ l3AddressEndpointWithLocation(ep4Mac, l2BdId2, ep4Ip, L3_CTX_ID), NODE1, node1Connector4);
+ List<AddressEndpointWithLocation> addrEps = createAddressEndpoints();
+ addrEps.add(l2DhcpEp);
+ addrEps.add(l3DhcpEp);
+ List<RendererEndpoint> rEps = new ArrayList<>();
+ // DHCP from different subnet
+ rEps.add(createRendEndpoint(rendererEndpointKey(l3DhcpEp.getKey()), SECURITY_GROUP.SERVER,
+ peerEndpointKey(l3AddrEp2.getKey()), peerEndpointKey(l3AddrEp3.getKey())));
+ // DHCP from local subnet
+ rEps.add(createRendEndpoint(rendererEndpointKey(l3AddrEp1.getKey()), SECURITY_GROUP.SERVER,
+ peerEndpointKey(l3AddrEp2.getKey()), peerEndpointKey(l3AddrEp3.getKey())));
+ rEps.add(createRendEndpoint(rendererEndpointKey(l3AddrEp2.getKey()), SECURITY_GROUP.CLIENT,
+ peerEndpointKey(l3AddrEp1.getKey()), peerEndpointKey(l3DhcpEp.getKey())));
+ rEps.add(createRendEndpoint(rendererEndpointKey(l3AddrEp3.getKey()), SECURITY_GROUP.CLIENT,
+ peerEndpointKey(l3AddrEp1.getKey()), peerEndpointKey(l3DhcpEp.getKey())));
+ ctx = super.createPolicyContext(addrEps, rEps, createRuleGroupsDhcpTest(), createForwarding());
+ List<Acl> acls = processChangedEndpoints(Sets.newHashSet(rendererEndpointKey(l3AddrEp2.getKey())), true);
+ Assert.assertEquals(acls.size(), 6);
+ acls.forEach(acl -> {
+ if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(node1Connector4).get())) {
Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
+ } else if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
+ Assert.assertEquals(6, acl.getAccessListEntries().getAce().size());
+ } else {
+ Assert.assertEquals(4, acl.getAccessListEntries().getAce().size());
}
});
+
}
/**
if (acl.getAclName().contains(VppPathMapper.interfacePathToInterfaceName(NODE1_CONNECTOR_1).get())) {
Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
} else {
- Assert.assertEquals(2, acl.getAccessListEntries().getAce().size());
+ // Entire ACL is removed elsewhere -> no update of rules needed.
+ Assert.assertEquals(6, acl.getAccessListEntries().getAce().size());
}
});
}
return new RuleGroupsBuilder().setRuleGroup(ImmutableList.<RuleGroup>of(rg1, rg2)).build();
}
- public RuleGroups createRuleGroups_DhcpTest() {
+ public RuleGroups createRuleGroupsDhcpTest() {
ParameterValue ipv4Param = new ParameterValueBuilder().setIntValue(EtherTypeClassifierDefinition.IPv4_VALUE)
.setName(new ParameterName(EtherTypeClassifierDefinition.ETHERTYPE_PARAM))
.build();
ParameterValue udpParam = new ParameterValueBuilder().setIntValue(IpProtoClassifierDefinition.UDP_VALUE)
.setName(new ParameterName(IpProtoClassifierDefinition.PROTO_PARAM))
.build();
+ ParameterValue srcDhcp = new ParameterValueBuilder().setIntValue(Long.valueOf(68))
+ .setName(new ParameterName(L4ClassifierDefinition.SRC_PORT_PARAM))
+ .build();
+ ParameterValue dstDhcp = new ParameterValueBuilder().setIntValue(Long.valueOf(67))
+ .setName(new ParameterName(L4ClassifierDefinition.DST_PORT_PARAM))
+ .build();
RuleGroup rg1 = createSimpleRuleGroups(ImmutableList.of(ipv4Param, tcpParam), IpProtoClassifierDefinition.ID,
SUBJECT_NAME);
- RuleGroup rg2 = createSimpleRuleGroups(ImmutableList.of(ipv4Param, udpParam), IpProtoClassifierDefinition.ID,
+ RuleGroup rg2 = createSimpleRuleGroups(ImmutableList.of(ipv4Param, udpParam, srcDhcp, dstDhcp), L4ClassifierDefinition.ID,
SUBJECT_NAME2);
return new RuleGroupsBuilder().setRuleGroup(ImmutableList.<RuleGroup>of(rg1, rg2)).build();
}
private RuleGroup createSimpleRuleGroups(List<ParameterValue> pv, ClassifierDefinitionId id,
SubjectName subjectName) {
final ClassifierName classifierName = new ClassifierName("cl-name");
- Classifier classifIn = new ClassifierBuilder().setClassifierDefinitionId(IpProtoClassifierDefinition.ID)
+ Classifier classifIn = new ClassifierBuilder().setClassifierDefinitionId(id)
.setDirection(Direction.In)
.setName(classifierName)
.setParameterValue(pv)
.build();
- Classifier classifOut = new ClassifierBuilder().setClassifierDefinitionId(IpProtoClassifierDefinition.ID)
+ Classifier classifOut = new ClassifierBuilder().setClassifierDefinitionId(id)
.setDirection(Direction.Out)
.setName(classifierName)
.setParameterValue(pv)