2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl;
11 import java.net.Inet4Address;
12 import java.net.Inet6Address;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.HashMap;
18 import java.util.List;
21 import javax.annotation.Nonnull;
23 import org.opendaylight.groupbasedpolicy.api.sf.AllowActionDefinition;
24 import org.opendaylight.groupbasedpolicy.renderer.util.AddressEndpointUtils;
25 import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.PolicyContext;
26 import org.opendaylight.groupbasedpolicy.renderer.vpp.policy.RendererResolvedPolicy;
27 import org.opendaylight.groupbasedpolicy.renderer.vpp.sf.SubjectFeatures;
28 import org.opendaylight.groupbasedpolicy.util.EndpointUtils;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Actions;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.ActionsBuilder;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpoint;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.L2BridgeDomain;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.SubnetAugmentRenderer;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.Subnet;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.RendererForwardingByTenant;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 import com.google.common.base.Optional;
55 * Transforms Renderer policy into access-list configuration for Honeycomb.
59 public class AccessListUtil {
61 private static final Logger LOG = LoggerFactory.getLogger(AccessListUtil.class);
62 private static final String UNDERSCORE = "_";
63 private static final String PERMIT_EXTERNAL_INGRESS = "permit_external_ingress";
64 private static final String PERMIT_EXTERNAL_EGRESS = "permit_external_egress";
65 private static final String DENY_INGRESS_IPV4 = "deny_ingress_ipv4";
66 private static final String DENY_INGRESS_IPV6 = "deny_ingress_ipv6";
67 private static final String DENY_EGRESS_IPV4 = "deny_egress_ipv4";
68 private static final String DENY_EGRESS_IPV6 = "deny_egress_ipv6";
70 public enum ACE_DIRECTION {
74 // hiding default public constructor
75 private AccessListUtil() {}
77 public static List<AccessListWrapper> resolveAclsOnInterface(RendererEndpointKey rEpKey, PolicyContext ctx) {
78 List<AccessListWrapper> aclWrappers = new ArrayList<>();
79 for (ACE_DIRECTION dir : new ACE_DIRECTION[] {ACE_DIRECTION.INGRESS, ACE_DIRECTION.EGRESS}) {
80 aclWrappers.add(buildAccessListWrappers(dir, ctx, rEpKey));
86 * @param policyDirection direction for which policy should be resolved. EP -> VPP = OUTBOUND, EP <- VPP = INBOUND
87 * @param ctx with cached data
88 * @param rEpKey key of EP for which to create ACLs.
89 * @return synchronization futures, so that INGRESS and EGRESS ACLS can be resolved in parallel.
91 private static AccessListWrapper buildAccessListWrappers(ACE_DIRECTION policyDirection, PolicyContext ctx,
92 RendererEndpointKey rEpKey) {
93 LOG.trace("Resolving policy for VPP renderer endpoint {} in a separate thread in {} direction.", rEpKey,
95 AccessListWrapper aclWrapper = AccessListUtil.ACE_DIRECTION.INGRESS
96 .equals(policyDirection) ? new IngressAccessListWrapper() : new EgressAccessListWrapper();
101 .filter(peerEpKey -> peerHasLocation(ctx, peerEpKey))
102 .forEach(peerEpKey -> {
103 ctx.getPolicyTable().get(rEpKey, peerEpKey).forEach(resolvedRules -> {
104 List<GbpAceBuilder> rules = new ArrayList<>();
105 LOG.debug("Resolving policy for {} and peer endpoint {}", rEpKey, peerEpKey);
106 Direction classifDir =
107 calculateClassifDirection(resolvedRules.getRendererEndpointParticipation(), policyDirection);
108 rules.addAll(resolveAclRulesFromPolicy(resolvedRules, classifDir,
109 rEpKey.getAddress() + UNDERSCORE + peerEpKey.getAddress()));
110 updateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true);
111 aclWrapper.writeRules(rules);
114 // resolve peers with no location
115 aclWrapper.writeRules(denyDomainSubnets(ctx, policyDirection));
116 // TODO currently any traffic heading to/from outside of managed domain is
117 // permitted for demonstration purposes
118 if (rEpKey.getContextType().isAssignableFrom(L2BridgeDomain.class) && findAddrEp(ctx, rEpKey) != null) {
119 Optional<GbpAceBuilder> allowExtAccess =
120 allowExternalNetworksForEp(findAddrEp(ctx, rEpKey), policyDirection);
121 if (allowExtAccess.isPresent()) {
122 aclWrapper.writeRule(allowExtAccess.get());
129 * Resolves direction for classifiers that will be applied to INBOUND or OUTBOUND direction.
131 * Rule is applied in INGRESS direction when participation is PROVIDER and classifier direction is OUT
133 * Rule is applied in INGRESS direction when participation is CONSUMER and classifier direction is IN
135 * INBOUND direction is applied otherwise.
139 * OUT classifier direction is resolved for INGRESS traffic when participation is PROVIDER
141 * OUT classifier direction is resolved for EGRESS traffic when participation is CONSUMER
143 * IN is resolved otherwise.
145 * @param participation provider or consumer
146 * @param direction EGRESS or INGRESS
147 * @return Direction that classifiers should match for given policy direction.
149 private static Direction calculateClassifDirection(EndpointPolicyParticipation participation, ACE_DIRECTION direction) {
150 if (EndpointPolicyParticipation.PROVIDER.equals(participation) && ACE_DIRECTION.INGRESS.equals(direction)) {
151 return Direction.Out;
153 if (EndpointPolicyParticipation.CONSUMER.equals(participation) && ACE_DIRECTION.EGRESS.equals(direction)) {
154 return Direction.Out;
159 private static void updateAddressesInRules(List<GbpAceBuilder> rules, RendererEndpointKey rEpKey,
160 PeerEndpointKey peerEpKey, PolicyContext ctx, ACE_DIRECTION policyDirection,
161 boolean resolveForLocationPeers) {
162 for (AddressMapper addrMapper : Arrays.asList(new SourceMapper(policyDirection),
163 new DestinationMapper(policyDirection))) {
164 if (peerHasLocation(ctx, peerEpKey) && resolveForLocationPeers) {
165 addrMapper.updateRules(rules, findAddrEp(ctx, rEpKey), findAddrEp(ctx, peerEpKey));
166 } else if (!peerHasLocation(ctx, peerEpKey) && !resolveForLocationPeers) {
167 addrMapper.updateExtRules(rules, findAddrEp(ctx, rEpKey), null);
172 private static boolean peerHasLocation(PolicyContext ctx, PeerEndpointKey peerEpKey) {
173 return ctx.getAddrEpByKey().get(
174 AddressEndpointUtils.fromPeerEpKey(peerEpKey)) != null;
177 private static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, RendererEndpointKey rEpKey) {
178 return ctx.getAddrEpByKey().get(
179 AddressEndpointUtils.fromRendererEpKey(rEpKey));
182 private static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, PeerEndpointKey rEpKey) {
183 return ctx.getAddrEpByKey().get(
184 AddressEndpointUtils.fromPeerEpKey(rEpKey));
187 /** Transform a resolved rule to ACE with corresponding classification and action fields
189 * @param resolvedPolicy resolved rules, with the same participation - provider or consumer
190 * @param direction rules matching corresponding direction will be collected
191 * @return resolved ACE entries
193 private static List<GbpAceBuilder> resolveAclRulesFromPolicy(RendererResolvedPolicy resolvedPolicy,
194 Direction direction, String namePasphrase) {
195 List<GbpAceBuilder> aclRules = new ArrayList<>();
196 for (ResolvedRule resolvedRule : resolvedPolicy.getRuleGroup().getRules()) {
197 Map<String, ParameterValue> params = resolveClassifParamsForDir(direction, resolvedRule.getClassifier());
198 if (params.isEmpty()) {
201 LOG.debug("Processing classifification params {} in resolved rule {}.", params,
202 resolvedRule.getName() + UNDERSCORE + namePasphrase);
203 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
204 resolveImplementedClassifForDir(direction, resolvedRule.getClassifier());
205 GbpAceBuilder aclRuleBuilder =
206 new GbpAceBuilder(resolvedRule.getName().getValue() + UNDERSCORE + namePasphrase);
207 boolean updated = classif != null && classif.updateMatch(aclRuleBuilder, params);
208 Optional<Actions> optAction = resolveActions(resolvedRule.getAction());
209 if (!optAction.isPresent() || !updated) {
210 LOG.error("Failed to process rule {}. Resolved parameters {}, resolved classifier. Actions resolved: {}"
211 + "{}.", resolvedRule.getName().getValue(), params, classif, optAction.isPresent());
214 aclRuleBuilder.setAction(optAction.get());
215 aclRules.add(aclRuleBuilder);
220 private static org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier resolveImplementedClassifForDir(
221 @Nonnull Direction direction, @Nonnull List<Classifier> classifiers) {
222 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier feasibleClassifier = null;
223 for (Classifier cl : classifiers) {
224 if (direction.equals(cl.getDirection()) || direction.equals(Direction.Bidirectional)) {
225 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
226 SubjectFeatures.getClassifier(cl.getClassifierDefinitionId());
227 if (feasibleClassifier == null) {
228 feasibleClassifier = classif;
230 if (classif.getParent() != null && classif.getParent().equals(feasibleClassifier)) {
231 feasibleClassifier = classif;
235 return feasibleClassifier;
238 private static Map<String, ParameterValue> resolveClassifParamsForDir(Direction direction,
239 List<Classifier> classifier) {
240 Map<String, ParameterValue> params = new HashMap<>();
242 .filter(classif -> direction.equals(classif.getDirection()) || direction.equals(Direction.Bidirectional))
243 .forEach(classif -> {
244 LOG.trace("Resolving parameters for classiifier: {} with direction", classif, direction);
245 classif.getParameterValue()
247 .filter(v -> params.get(v.getName().getValue()) == null) // not unique
248 .filter(v -> v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null)
249 .forEach(v -> params.put(v.getName().getValue(), v));
250 LOG.trace("Resolved parameters {} for classiifier: {} with direction {}", params, classif, direction);
255 private static Optional<Actions> resolveActions(List<Action> actions) {
256 for (Action action : actions) {
257 if (AllowActionDefinition.ID
258 .equals(action.getActionDefinitionId())) {
259 LOG.trace("Applying supported action: {}", action);
261 .of(new ActionsBuilder().setPacketHandling(new PermitBuilder().setPermit(true).build()).build());
264 LOG.warn("No supported action found among actions: {}", actions);
265 return Optional.absent();
269 * so far any traffic heading to/from outside of managed domain is permitted for demonstration
271 * TODO initial workaround for external networking
273 private static Optional<GbpAceBuilder> allowExternalNetworksForEp(@Nonnull AddressEndpointWithLocation addrEp,
274 AccessListUtil.ACE_DIRECTION dir) {
275 List<ParentEndpoint> parentEndpoints = EndpointUtils.getParentEndpoints(addrEp.getParentEndpointChoice());
276 if (parentEndpoints.isEmpty()) {
277 return Optional.absent();
279 for (ParentEndpoint parentEp : parentEndpoints) {
282 byName = InetAddress.getByName(substringBeforeSlash(parentEp.getAddress()));
283 } catch (UnknownHostException e) {
284 LOG.error("Failed to parse IP address {}", e);
285 return Optional.absent();
287 if (byName instanceof Inet4Address) {
288 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
289 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
290 new Ipv4Prefix(parentEp.getAddress()), null).setPermit());
292 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
293 new Ipv4Prefix(parentEp.getAddress())).setPermit());
295 } else if (byName instanceof Inet6Address) {
296 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
297 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
298 new Ipv6Prefix(parentEp.getAddress()), null).setPermit());
300 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
301 new Ipv6Prefix(parentEp.getAddress())).setPermit());
305 return Optional.absent();
309 * Helps stripping address part of a CIDR
311 private static String substringBeforeSlash(String address) {
312 return (address.contains("/") && address.split("/").length > 0) ? address.split("/")[0] : address;
315 private static List<GbpAceBuilder> denyDomainSubnets(@Nonnull PolicyContext ctx, @Nonnull ACE_DIRECTION policyDirection) {
316 List<GbpAceBuilder> aclRuleBuilders = new ArrayList<>();
317 for (RendererForwardingByTenant rf : ctx.getPolicy()
319 .getRendererForwarding()
320 .getRendererForwardingByTenant()) {
321 rf.getRendererNetworkDomain()
323 .filter(rnd -> org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.Subnet.class
324 .equals(rnd.getNetworkDomainType()))
326 SubnetAugmentRenderer subnetAug = rnd.getAugmentation(SubnetAugmentRenderer.class);
327 // subnetAug should not be null
328 subnetAug.getSubnet();
329 if (policyDirection.equals(ACE_DIRECTION.INGRESS)) {
330 aclRuleBuilders.add(denyIngressTrafficForPrefix(subnetAug.getSubnet()));
333 aclRuleBuilders.add(denyEgressTrafficForPrefix(subnetAug.getSubnet()));
337 return aclRuleBuilders;
340 private static GbpAceBuilder denyEgressTrafficForPrefix(Subnet subnet) {
341 IpPrefix ipPrefix = subnet.getIpPrefix();
342 if (ipPrefix.getIpv4Prefix() != null) {
343 return new GbpAceBuilder(DENY_EGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
344 .setIpAddresses(ipPrefix.getIpv4Prefix(), null).setDeny();
345 } else if (ipPrefix.getIpv6Prefix() != null) {
346 return new GbpAceBuilder(DENY_EGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
347 .setIpAddresses(ipPrefix.getIpv6Prefix(), null).setDeny();
349 throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
352 static void setSourceL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
353 InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
354 if (addr instanceof Inet6Address) {
355 rule.setIpAddresses(new Ipv6Prefix(address), null);
357 rule.setIpAddresses(new Ipv4Prefix(address), null);
361 static void setDestinationL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
362 InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
363 if (addr instanceof Inet6Address) {
364 rule.setIpAddresses(null, new Ipv6Prefix(address));
366 rule.setIpAddresses(null, new Ipv4Prefix(address));
370 static GbpAceBuilder denyIngressTrafficForPrefix(Subnet subnet) {
371 IpPrefix ipPrefix = subnet.getIpPrefix();
372 if (ipPrefix.getIpv4Prefix() != null) {
373 return new GbpAceBuilder(DENY_INGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
374 .setIpAddresses(null, ipPrefix.getIpv4Prefix()).setDeny();
375 } else if (ipPrefix.getIpv6Prefix() != null) {
376 return new GbpAceBuilder(DENY_INGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
377 .setIpAddresses(null, ipPrefix.getIpv6Prefix()).setDeny();
379 throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());