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.SubnetAugmentRenderer;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.has.subnet.Subnet;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.EndpointPolicyParticipation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.RendererEndpointKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.endpoints.renderer.endpoint.PeerEndpointKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.renderer.forwarding.RendererForwardingByTenant;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.actions.Action;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.classifiers.Classifier;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.resolved.policy.rev150828.has.resolved.rules.ResolvedRule;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 import com.google.common.base.Optional;
54 * Transforms Renderer policy into access-list configuration for Honeycomb.
58 public class AccessListUtil {
60 private static final Logger LOG = LoggerFactory.getLogger(AccessListUtil.class);
61 private static final String UNDERSCORE = "_";
62 private static final String PERMIT_EXTERNAL_INGRESS = "permit_external_ingress";
63 private static final String PERMIT_EXTERNAL_EGRESS = "permit_external_egress";
64 private static final String DENY_INGRESS_IPV4 = "deny_ingress_ipv4";
65 private static final String DENY_INGRESS_IPV6 = "deny_ingress_ipv6";
66 private static final String DENY_EGRESS_IPV4 = "deny_egress_ipv4";
67 private static final String DENY_EGRESS_IPV6 = "deny_egress_ipv6";
69 public enum ACE_DIRECTION {
73 // hiding default public constructor
74 private AccessListUtil() {}
76 static void configureLocalRules(PolicyContext ctx, RendererEndpointKey rEpKey, ACE_DIRECTION policyDirection,
77 AccessListWrapper aclWrapper) {
82 .filter(peerEpKey -> peerHasLocation(ctx, peerEpKey))
83 .forEach(peerEpKey -> {
84 ctx.getPolicyTable().get(rEpKey, peerEpKey).forEach(resolvedRules -> {
85 List<GbpAceBuilder> rules = new ArrayList<>();
86 LOG.debug("Resolving policy for {} and peer endpoint {}", rEpKey, peerEpKey);
87 Direction classifDir = calculateClassifDirection(resolvedRules.getRendererEndpointParticipation(),
89 rules.addAll(resolveAclRulesFromPolicy(resolvedRules, classifDir,
90 rEpKey.getAddress() + UNDERSCORE + peerEpKey.getAddress()));
91 updateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true);
92 aclWrapper.writeRules(rules);
99 * Resolves direction for classifiers that will be applied to INBOUND or OUTBOUND direction.
101 * Rule is applied in INGRESS direction when participation is PROVIDER and classifier direction is OUT
103 * Rule is applied in INGRESS direction when participation is CONSUMER and classifier direction is IN
105 * INBOUND direction is applied otherwise.
109 * OUT classifier direction is resolved for INGRESS traffic when participation is PROVIDER
111 * OUT classifier direction is resolved for EGRESS traffic when participation is CONSUMER
113 * IN is resolved otherwise.
115 * @param participation provider or consumer
116 * @param direction EGRESS or INGRESS
117 * @return Direction that classifiers should match for given policy direction.
119 private static Direction calculateClassifDirection(EndpointPolicyParticipation participation, ACE_DIRECTION direction) {
120 if (EndpointPolicyParticipation.PROVIDER.equals(participation) && ACE_DIRECTION.INGRESS.equals(direction)) {
121 return Direction.Out;
123 if (EndpointPolicyParticipation.CONSUMER.equals(participation) && ACE_DIRECTION.EGRESS.equals(direction)) {
124 return Direction.Out;
129 private static void updateAddressesInRules(List<GbpAceBuilder> rules, RendererEndpointKey rEpKey,
130 PeerEndpointKey peerEpKey, PolicyContext ctx, ACE_DIRECTION policyDirection,
131 boolean resolveForLocationPeers) {
132 for (AddressMapper addrMapper : Arrays.asList(new SourceMapper(policyDirection),
133 new DestinationMapper(policyDirection))) {
134 if (peerHasLocation(ctx, peerEpKey) && resolveForLocationPeers) {
135 addrMapper.updateRules(rules, findAddrEp(ctx, rEpKey), findAddrEp(ctx, peerEpKey));
136 } else if (!peerHasLocation(ctx, peerEpKey) && !resolveForLocationPeers) {
137 addrMapper.updateExtRules(rules, findAddrEp(ctx, rEpKey), null);
142 private static boolean peerHasLocation(PolicyContext ctx, PeerEndpointKey peerEpKey) {
143 return ctx.getAddrEpByKey().get(
144 AddressEndpointUtils.fromPeerEpKey(peerEpKey)) != null;
147 static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, RendererEndpointKey rEpKey) {
148 return ctx.getAddrEpByKey().get(
149 AddressEndpointUtils.fromRendererEpKey(rEpKey));
152 private static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, PeerEndpointKey rEpKey) {
153 return ctx.getAddrEpByKey().get(
154 AddressEndpointUtils.fromPeerEpKey(rEpKey));
157 /** Transform a resolved rule to ACE with corresponding classification and action fields
159 * @param resolvedPolicy resolved rules, with the same participation - provider or consumer
160 * @param direction rules matching corresponding direction will be collected
161 * @return resolved ACE entries
163 private static List<GbpAceBuilder> resolveAclRulesFromPolicy(RendererResolvedPolicy resolvedPolicy,
164 Direction direction, String namePasphrase) {
165 List<GbpAceBuilder> aclRules = new ArrayList<>();
166 for (ResolvedRule resolvedRule : resolvedPolicy.getRuleGroup().getRules()) {
167 Map<String, ParameterValue> params = resolveClassifParamsForDir(direction, resolvedRule.getClassifier());
168 if (params.isEmpty()) {
171 LOG.debug("Processing classifification params {} in resolved rule {}.", params,
172 resolvedRule.getName() + UNDERSCORE + namePasphrase);
173 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
174 resolveImplementedClassifForDir(direction, resolvedRule.getClassifier());
175 GbpAceBuilder aclRuleBuilder =
176 new GbpAceBuilder(resolvedRule.getName().getValue() + UNDERSCORE + namePasphrase);
177 boolean updated = classif != null && classif.updateMatch(aclRuleBuilder, params);
178 Optional<Actions> optAction = resolveActions(resolvedRule.getAction());
179 if (!optAction.isPresent() || !updated) {
180 LOG.error("Failed to process rule {}. Resolved parameters {}, resolved classifier. Actions resolved: {}"
181 + "{}.", resolvedRule.getName().getValue(), params, classif, optAction.isPresent());
184 aclRuleBuilder.setAction(optAction.get());
185 aclRules.add(aclRuleBuilder);
190 private static org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier resolveImplementedClassifForDir(
191 @Nonnull Direction direction, @Nonnull List<Classifier> classifiers) {
192 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier feasibleClassifier = null;
193 for (Classifier cl : classifiers) {
194 if (direction.equals(cl.getDirection()) || direction.equals(Direction.Bidirectional)) {
195 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
196 SubjectFeatures.getClassifier(cl.getClassifierDefinitionId());
197 if (feasibleClassifier == null) {
198 feasibleClassifier = classif;
200 if (classif.getParent() != null && classif.getParent().equals(feasibleClassifier)) {
201 feasibleClassifier = classif;
205 return feasibleClassifier;
208 private static Map<String, ParameterValue> resolveClassifParamsForDir(Direction direction,
209 List<Classifier> classifier) {
210 Map<String, ParameterValue> params = new HashMap<>();
212 .filter(classif -> direction.equals(classif.getDirection()) || direction.equals(Direction.Bidirectional))
213 .forEach(classif -> {
214 LOG.trace("Resolving parameters for classiifier: {} with direction", classif, direction);
215 classif.getParameterValue()
217 .filter(v -> params.get(v.getName().getValue()) == null) // not unique
218 .filter(v -> v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null)
219 .forEach(v -> params.put(v.getName().getValue(), v));
220 LOG.trace("Resolved parameters {} for classiifier: {} with direction {}", params, classif, direction);
225 private static Optional<Actions> resolveActions(List<Action> actions) {
226 for (Action action : actions) {
227 if (AllowActionDefinition.ID
228 .equals(action.getActionDefinitionId())) {
229 LOG.trace("Applying supported action: {}", action);
231 .of(new ActionsBuilder().setPacketHandling(new PermitBuilder().setPermit(true).build()).build());
234 LOG.warn("No supported action found among actions: {}", actions);
235 return Optional.absent();
239 * so far any traffic heading to/from outside of managed domain is permitted for demonstration
241 * TODO initial workaround for external networking
243 static Optional<GbpAceBuilder> allowExternalNetworksForEp(@Nonnull AddressEndpointWithLocation addrEp,
244 AccessListUtil.ACE_DIRECTION dir) {
245 List<ParentEndpoint> parentEndpoints = EndpointUtils.getParentEndpoints(addrEp.getParentEndpointChoice());
246 if (parentEndpoints.isEmpty()) {
247 return Optional.absent();
249 for (ParentEndpoint parentEp : parentEndpoints) {
252 byName = InetAddress.getByName(substringBeforeSlash(parentEp.getAddress()));
253 } catch (UnknownHostException e) {
254 LOG.error("Failed to parse IP address {}", e);
255 return Optional.absent();
257 if (byName instanceof Inet4Address) {
258 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
259 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
260 new Ipv4Prefix(parentEp.getAddress()), null).setPermit());
262 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
263 new Ipv4Prefix(parentEp.getAddress())).setPermit());
265 } else if (byName instanceof Inet6Address) {
266 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
267 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
268 new Ipv6Prefix(parentEp.getAddress()), null).setPermit());
270 return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
271 new Ipv6Prefix(parentEp.getAddress())).setPermit());
275 return Optional.absent();
279 * Helps stripping address part of a CIDR
281 private static String substringBeforeSlash(String address) {
282 return (address.contains("/") && address.split("/").length > 0) ? address.split("/")[0] : address;
285 static List<GbpAceBuilder> denyDomainSubnets(@Nonnull PolicyContext ctx, @Nonnull ACE_DIRECTION policyDirection) {
286 List<GbpAceBuilder> aclRuleBuilders = new ArrayList<>();
287 for (RendererForwardingByTenant rf : ctx.getPolicy()
289 .getRendererForwarding()
290 .getRendererForwardingByTenant()) {
291 rf.getRendererNetworkDomain()
293 .filter(rnd -> org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.Subnet.class
294 .equals(rnd.getNetworkDomainType()))
296 SubnetAugmentRenderer subnetAug = rnd.getAugmentation(SubnetAugmentRenderer.class);
297 // subnetAug should not be null
298 subnetAug.getSubnet();
299 if (policyDirection.equals(ACE_DIRECTION.INGRESS) && subnetAug.getSubnet().isIsTenant()) {
300 aclRuleBuilders.add(denyIngressTrafficForPrefix(subnetAug.getSubnet()));
302 else if (subnetAug.getSubnet().isIsTenant()) {
303 aclRuleBuilders.add(denyEgressTrafficForPrefix(subnetAug.getSubnet()));
307 return aclRuleBuilders;
310 private static GbpAceBuilder denyEgressTrafficForPrefix(Subnet subnet) {
311 IpPrefix ipPrefix = subnet.getIpPrefix();
312 if (ipPrefix.getIpv4Prefix() != null) {
313 return new GbpAceBuilder(DENY_EGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
314 .setIpAddresses(ipPrefix.getIpv4Prefix(), null).setDeny();
315 } else if (ipPrefix.getIpv6Prefix() != null) {
316 return new GbpAceBuilder(DENY_EGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
317 .setIpAddresses(ipPrefix.getIpv6Prefix(), null).setDeny();
319 throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
322 static void setSourceL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
323 InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
324 if (addr instanceof Inet6Address) {
325 rule.setIpAddresses(new Ipv6Prefix(address), null);
327 rule.setIpAddresses(new Ipv4Prefix(address), null);
331 static void setDestinationL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
332 InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
333 if (addr instanceof Inet6Address) {
334 rule.setIpAddresses(null, new Ipv6Prefix(address));
336 rule.setIpAddresses(null, new Ipv4Prefix(address));
340 static GbpAceBuilder denyIngressTrafficForPrefix(Subnet subnet) {
341 IpPrefix ipPrefix = subnet.getIpPrefix();
342 if (ipPrefix.getIpv4Prefix() != null) {
343 return new GbpAceBuilder(DENY_INGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
344 .setIpAddresses(null, ipPrefix.getIpv4Prefix()).setDeny();
345 } else if (ipPrefix.getIpv6Prefix() != null) {
346 return new GbpAceBuilder(DENY_INGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
347 .setIpAddresses(null, ipPrefix.getIpv6Prefix()).setDeny();
349 throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());