Policy exclusions & parallel netconf transactions
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / policy / acl / AccessListUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.policy.acl;
10
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;
19 import java.util.Map;
20
21 import javax.annotation.Nonnull;
22
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.common.rev140421.RuleName;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.SubnetAugmentRenderer;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.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;
51
52 import com.google.common.base.Optional;
53
54  /*
55   * Transforms Renderer policy into access-list configuration for Honeycomb.
56   *
57   * */
58
59 public class AccessListUtil {
60
61     private static final Logger LOG = LoggerFactory.getLogger(AccessListUtil.class);
62     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";
69
70     public enum ACE_DIRECTION {
71         INGRESS, EGRESS
72     }
73
74     // hiding default public constructor
75     private AccessListUtil() {}
76
77     static void configureLocalRules(PolicyContext ctx, RendererEndpointKey rEpKey, ACE_DIRECTION policyDirection,
78             AccessListWrapper aclWrapper) {
79         ctx.getPolicyTable()
80             .row(rEpKey)
81             .keySet()
82             .stream()
83             .filter(peerEpKey -> peerHasLocation(ctx, peerEpKey))
84             .forEach(peerEpKey -> {
85                 ctx.getPolicyTable().get(rEpKey, peerEpKey).forEach(resolvedRules -> {
86                     List<GbpAceBuilder> rules = new ArrayList<>();
87                     Direction classifDir = calculateClassifDirection(resolvedRules.getRendererEndpointParticipation(),
88                             policyDirection);
89                     rules.addAll(resolveAclRulesFromPolicy(resolvedRules, classifDir, rEpKey, peerEpKey));
90                     updateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true);
91                     aclWrapper.writeRules(rules);
92
93                 });
94             });
95     }
96
97     /**
98      * Resolves direction for classifiers that will be applied to INBOUND or OUTBOUND direction.
99      * </p>
100      * Rule is applied in INGRESS direction when participation is PROVIDER and classifier direction is OUT
101      * </p>
102      * Rule is applied in INGRESS direction when participation is CONSUMER and classifier direction is IN
103      * </p>
104      * INBOUND direction is applied otherwise.
105      * </p>
106      * Based on this
107      * </p>
108      * OUT classifier direction is resolved for INGRESS traffic when participation is PROVIDER
109      * </p>
110      * OUT classifier direction is resolved for EGRESS traffic when participation is CONSUMER
111      * </p>
112      * IN is resolved otherwise.
113      * </p>
114      * @param participation provider or consumer
115      * @param direction EGRESS or INGRESS
116      * @return Direction that classifiers should match for given policy direction.
117      */
118      static Direction calculateClassifDirection(EndpointPolicyParticipation participation, ACE_DIRECTION direction) {
119         if (EndpointPolicyParticipation.PROVIDER.equals(participation) && ACE_DIRECTION.INGRESS.equals(direction)) {
120             return Direction.Out;
121         }
122         if (EndpointPolicyParticipation.CONSUMER.equals(participation) && ACE_DIRECTION.EGRESS.equals(direction)) {
123             return Direction.Out;
124         }
125         return Direction.In;
126     }
127
128     static void updateAddressesInRules(List<GbpAceBuilder> rules, RendererEndpointKey rEpKey,
129             PeerEndpointKey peerEpKey, PolicyContext ctx, ACE_DIRECTION policyDirection,
130             boolean resolveForLocationPeers) {
131         for (AddressMapper addrMapper : Arrays.asList(new SourceMapper(policyDirection),
132                 new DestinationMapper(policyDirection))) {
133             if (peerHasLocation(ctx, peerEpKey) && resolveForLocationPeers) {
134                 addrMapper.updateRules(rules, findAddrEp(ctx, rEpKey), findAddrEp(ctx, peerEpKey));
135             } else if (!peerHasLocation(ctx, peerEpKey) && !resolveForLocationPeers) {
136                 addrMapper.updateExtRules(rules, findAddrEp(ctx, rEpKey), null);
137             }
138         }
139     }
140
141     private static boolean peerHasLocation(PolicyContext ctx, PeerEndpointKey peerEpKey) {
142         return ctx.getAddrEpByKey().get(
143                 AddressEndpointUtils.fromPeerEpKey(peerEpKey)) != null;
144     }
145
146     static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, RendererEndpointKey rEpKey) {
147         return ctx.getAddrEpByKey().get(
148                 AddressEndpointUtils.fromRendererEpKey(rEpKey));
149     }
150
151     private static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, PeerEndpointKey rEpKey) {
152         return ctx.getAddrEpByKey().get(
153                 AddressEndpointUtils.fromPeerEpKey(rEpKey));
154     }
155
156     /** Transform a resolved rule to ACE with corresponding classification and action fields
157      *
158      * @param resolvedPolicy resolved rules, with the same participation - provider or consumer
159      * @param direction rules matching corresponding direction will be collected
160      * @return resolved ACE entries
161      */
162
163      static @Nonnull String resolveAceName(@Nonnull RuleName ruleName, @Nonnull RendererEndpointKey key,
164             @Nonnull PeerEndpointKey peer) {
165         return ruleName.getValue() + "_" + key.getAddress() + "_" + peer.getAddress();
166     }
167
168     private static List<GbpAceBuilder> resolveAclRulesFromPolicy(RendererResolvedPolicy resolvedPolicy,
169             Direction direction, RendererEndpointKey r, PeerEndpointKey p) {
170         List<GbpAceBuilder> aclRules = new ArrayList<>();
171         for (ResolvedRule resolvedRule : resolvedPolicy.getRuleGroup().getRules()) {
172             Optional<GbpAceBuilder> resolveAce = resolveAceClassifersAndAction(resolvedRule, direction, resolveAceName(resolvedRule.getName(), r, p));
173             if(resolveAce.isPresent()) {
174                 aclRules.add(resolveAce.get());
175             }
176         }
177         return aclRules;
178     }
179
180     public static Optional<GbpAceBuilder> resolveAceClassifersAndAction(ResolvedRule resolvedRule, Direction direction, String ruleName) {
181         Map<String, ParameterValue> params = resolveClassifParamsForDir(direction, resolvedRule.getClassifier());
182         if (params.isEmpty()) {
183             return Optional.absent();
184         }
185         org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
186                 resolveImplementedClassifForDir(direction, resolvedRule.getClassifier());
187         GbpAceBuilder aclRuleBuilder = new GbpAceBuilder(ruleName);
188                 //new GbpAceBuilder(resolvedRule.getName().getValue() + UNDERSCORE + namePasphrase);
189         boolean updated = classif != null && classif.updateMatch(aclRuleBuilder, params);
190         Optional<Actions> optAction = resolveActions(resolvedRule.getAction());
191         if (!optAction.isPresent() || !updated) {
192             LOG.error("Failed to process rule {}. Resolved parameters {}, resolved classifier. Actions resolved: {}"
193                     + "{}.", resolvedRule.getName().getValue(), params, classif, optAction.isPresent());
194             return Optional.absent();
195         }
196         aclRuleBuilder.setAction(optAction.get());
197         return Optional.of(aclRuleBuilder);
198     }
199
200     private static org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier resolveImplementedClassifForDir(
201             @Nonnull Direction direction, @Nonnull List<Classifier> classifiers) {
202         org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier feasibleClassifier = null;
203         for (Classifier cl : classifiers) {
204             if (direction.equals(cl.getDirection()) || direction.equals(Direction.Bidirectional)) {
205                 org.opendaylight.groupbasedpolicy.renderer.vpp.sf.Classifier classif =
206                         SubjectFeatures.getClassifier(cl.getClassifierDefinitionId());
207                 if (feasibleClassifier == null) {
208                     feasibleClassifier = classif;
209                 }
210                 if (classif.getParent() != null && classif.getParent().equals(feasibleClassifier)) {
211                     feasibleClassifier = classif;
212                 }
213             }
214         }
215         return feasibleClassifier;
216     }
217
218     private static Map<String, ParameterValue> resolveClassifParamsForDir(Direction direction,
219             List<Classifier> classifier) {
220         Map<String, ParameterValue> params = new HashMap<>();
221         classifier.stream()
222             .filter(classif -> direction.equals(classif.getDirection()) || direction.equals(Direction.Bidirectional))
223             .forEach(classif -> {
224                 classif.getParameterValue()
225                     .stream()
226                     .filter(v -> params.get(v.getName().getValue()) == null) // not unique
227                     .filter(v -> v.getIntValue() != null || v.getStringValue() != null || v.getRangeValue() != null)
228                     .forEach(v -> params.put(v.getName().getValue(), v));
229             });
230         return params;
231     }
232
233     private static Optional<Actions> resolveActions(List<Action> actions) {
234         for (Action action : actions) {
235             if (AllowActionDefinition.ID
236                 .equals(action.getActionDefinitionId())) {
237                 return Optional
238                     .of(new ActionsBuilder().setPacketHandling(new PermitBuilder().setPermit(true).build()).build());
239             }
240         }
241         return Optional.absent();
242     }
243
244     /*
245      * so far any traffic heading to/from outside of managed domain is permitted for demonstration
246      * purposes
247      * TODO initial workaround for external networking
248      */
249    static Optional<GbpAceBuilder> allowExternalNetworksForEp(@Nonnull AddressEndpointWithLocation addrEp,
250             AccessListUtil.ACE_DIRECTION dir) {
251         List<ParentEndpoint> parentEndpoints = EndpointUtils.getParentEndpoints(addrEp.getParentEndpointChoice());
252         if (parentEndpoints.isEmpty()) {
253             return Optional.absent();
254         }
255         for (ParentEndpoint parentEp : parentEndpoints) {
256             InetAddress byName;
257             try {
258                 byName = InetAddress.getByName(substringBeforeSlash(parentEp.getAddress()));
259             } catch (UnknownHostException e) {
260                 LOG.error("Failed to parse IP address {}", e);
261                 return Optional.absent();
262             }
263             if (byName instanceof Inet4Address) {
264                 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
265                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
266                             new Ipv4Prefix(parentEp.getAddress()), null).setPermit());
267                 } else {
268                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
269                             new Ipv4Prefix(parentEp.getAddress())).setPermit());
270                 }
271             } else if (byName instanceof Inet6Address) {
272                 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(dir)) {
273                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_INGRESS).setIpAddresses(
274                             new Ipv6Prefix(parentEp.getAddress()), null).setPermit());
275                 } else {
276                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
277                             new Ipv6Prefix(parentEp.getAddress())).setPermit());
278                 }
279             }
280         }
281         return Optional.absent();
282     }
283
284     /**
285      * Helps stripping address part of a CIDR
286      */
287     private static String substringBeforeSlash(String address) {
288         return (address.contains("/") && address.split("/").length > 0) ? address.split("/")[0] : address;
289     }
290
291     static List<GbpAceBuilder> denyDomainSubnets(@Nonnull PolicyContext ctx, @Nonnull ACE_DIRECTION policyDirection) {
292         List<GbpAceBuilder> aclRuleBuilders = new ArrayList<>();
293         for (RendererForwardingByTenant rf : ctx.getPolicy()
294             .getConfiguration()
295             .getRendererForwarding()
296             .getRendererForwardingByTenant()) {
297             rf.getRendererNetworkDomain()
298                 .stream()
299                 .filter(rnd -> org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.Subnet.class
300                     .equals(rnd.getNetworkDomainType()))
301                 .forEach(rnd -> {
302                     SubnetAugmentRenderer subnetAug = rnd.getAugmentation(SubnetAugmentRenderer.class);
303                     // subnetAug should not be null
304                     subnetAug.getSubnet();
305                     if (policyDirection.equals(ACE_DIRECTION.INGRESS) && subnetAug.getSubnet().isIsTenant()) {
306                         aclRuleBuilders.add(denyIngressTrafficForPrefix(subnetAug.getSubnet()));
307                     }
308                     else if (subnetAug.getSubnet().isIsTenant()) {
309                         aclRuleBuilders.add(denyEgressTrafficForPrefix(subnetAug.getSubnet()));
310                     }
311                 });
312         }
313         return aclRuleBuilders;
314     }
315
316     private static GbpAceBuilder denyEgressTrafficForPrefix(Subnet subnet) {
317         IpPrefix ipPrefix = subnet.getIpPrefix();
318         if (ipPrefix.getIpv4Prefix() != null) {
319             return new GbpAceBuilder(DENY_EGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
320                 .setIpAddresses(ipPrefix.getIpv4Prefix(), null).setDeny();
321         } else if (ipPrefix.getIpv6Prefix() != null) {
322             return new GbpAceBuilder(DENY_EGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
323                 .setIpAddresses(ipPrefix.getIpv6Prefix(), null).setDeny();
324         }
325         throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
326     }
327
328     static void setSourceL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
329         InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
330         if (addr instanceof Inet6Address) {
331             rule.setIpAddresses(new Ipv6Prefix(address), null);
332         } else {
333             rule.setIpAddresses(new Ipv4Prefix(address), null);
334         }
335     }
336
337     static void setDestinationL3Address(GbpAceBuilder rule, String address) throws UnknownHostException {
338         InetAddress addr = InetAddress.getByName(substringBeforeSlash(address));
339         if (addr instanceof Inet6Address) {
340             rule.setIpAddresses(null, new Ipv6Prefix(address));
341         } else {
342             rule.setIpAddresses(null, new Ipv4Prefix(address));
343         }
344     }
345
346     static GbpAceBuilder denyIngressTrafficForPrefix(Subnet subnet) {
347         IpPrefix ipPrefix = subnet.getIpPrefix();
348         if (ipPrefix.getIpv4Prefix() != null) {
349             return new GbpAceBuilder(DENY_INGRESS_IPV4 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
350                 .setIpAddresses(null, ipPrefix.getIpv4Prefix()).setDeny();
351         } else if (ipPrefix.getIpv6Prefix() != null) {
352             return new GbpAceBuilder(DENY_INGRESS_IPV6 + UNDERSCORE + String.valueOf(ipPrefix.getValue()))
353                 .setIpAddresses(null, ipPrefix.getIpv6Prefix()).setDeny();
354         }
355         throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
356     }
357 }