Bug 8228 - metadata service fix made cleaner
[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.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;
50
51 import com.google.common.base.Optional;
52
53  /*
54   * Transforms Renderer policy into access-list configuration for Honeycomb.
55   *
56   * */
57
58 public class AccessListUtil {
59
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";
68
69     public enum ACE_DIRECTION {
70         INGRESS, EGRESS
71     }
72
73     // hiding default public constructor
74     private AccessListUtil() {}
75
76     static void configureLocalRules(PolicyContext ctx, RendererEndpointKey rEpKey, ACE_DIRECTION policyDirection,
77             AccessListWrapper aclWrapper) {
78         ctx.getPolicyTable()
79             .row(rEpKey)
80             .keySet()
81             .stream()
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(),
88                             policyDirection);
89                     rules.addAll(resolveAclRulesFromPolicy(resolvedRules, classifDir,
90                             rEpKey.getAddress() + UNDERSCORE + peerEpKey.getAddress()));
91                     updateAddressesInRules(rules, rEpKey, peerEpKey, ctx, policyDirection, true);
92                     aclWrapper.writeRules(rules);
93
94                 });
95             });
96     }
97
98     /**
99      * Resolves direction for classifiers that will be applied to INBOUND or OUTBOUND direction.
100      * </p>
101      * Rule is applied in INGRESS direction when participation is PROVIDER and classifier direction is OUT
102      * </p>
103      * Rule is applied in INGRESS direction when participation is CONSUMER and classifier direction is IN
104      * </p>
105      * INBOUND direction is applied otherwise.
106      * </p>
107      * Based on this
108      * </p>
109      * OUT classifier direction is resolved for INGRESS traffic when participation is PROVIDER
110      * </p>
111      * OUT classifier direction is resolved for EGRESS traffic when participation is CONSUMER
112      * </p>
113      * IN is resolved otherwise.
114      * </p>
115      * @param participation provider or consumer
116      * @param direction EGRESS or INGRESS
117      * @return Direction that classifiers should match for given policy direction.
118      */
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;
122         }
123         if (EndpointPolicyParticipation.CONSUMER.equals(participation) && ACE_DIRECTION.EGRESS.equals(direction)) {
124             return Direction.Out;
125         }
126         return Direction.In;
127     }
128
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);
138             }
139         }
140     }
141
142     private static boolean peerHasLocation(PolicyContext ctx, PeerEndpointKey peerEpKey) {
143         return ctx.getAddrEpByKey().get(
144                 AddressEndpointUtils.fromPeerEpKey(peerEpKey)) != null;
145     }
146
147     static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, RendererEndpointKey rEpKey) {
148         return ctx.getAddrEpByKey().get(
149                 AddressEndpointUtils.fromRendererEpKey(rEpKey));
150     }
151
152     private static AddressEndpointWithLocation findAddrEp(PolicyContext ctx, PeerEndpointKey rEpKey) {
153         return ctx.getAddrEpByKey().get(
154                 AddressEndpointUtils.fromPeerEpKey(rEpKey));
155     }
156
157     /** Transform a resolved rule to ACE with corresponding classification and action fields
158      *
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
162      */
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()) {
169                 continue;
170             }
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());
182                 continue;
183             }
184             aclRuleBuilder.setAction(optAction.get());
185             aclRules.add(aclRuleBuilder);
186         }
187         return aclRules;
188     }
189
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;
199                 }
200                 if (classif.getParent() != null && classif.getParent().equals(feasibleClassifier)) {
201                     feasibleClassifier = classif;
202                 }
203             }
204         }
205         return feasibleClassifier;
206     }
207
208     private static Map<String, ParameterValue> resolveClassifParamsForDir(Direction direction,
209             List<Classifier> classifier) {
210         Map<String, ParameterValue> params = new HashMap<>();
211         classifier.stream()
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()
216                     .stream()
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);
221             });
222         return params;
223     }
224
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);
230                 return Optional
231                     .of(new ActionsBuilder().setPacketHandling(new PermitBuilder().setPermit(true).build()).build());
232             }
233         }
234         LOG.warn("No supported action found among actions: {}", actions);
235         return Optional.absent();
236     }
237
238     /*
239      * so far any traffic heading to/from outside of managed domain is permitted for demonstration
240      * purposes
241      * TODO initial workaround for external networking
242      */
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();
248         }
249         for (ParentEndpoint parentEp : parentEndpoints) {
250             InetAddress byName;
251             try {
252                 byName = InetAddress.getByName(substringBeforeSlash(parentEp.getAddress()));
253             } catch (UnknownHostException e) {
254                 LOG.error("Failed to parse IP address {}", e);
255                 return Optional.absent();
256             }
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());
261                 } else {
262                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
263                             new Ipv4Prefix(parentEp.getAddress())).setPermit());
264                 }
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());
269                 } else {
270                     return Optional.of(new GbpAceBuilder(PERMIT_EXTERNAL_EGRESS).setIpAddresses(null,
271                             new Ipv6Prefix(parentEp.getAddress())).setPermit());
272                 }
273             }
274         }
275         return Optional.absent();
276     }
277
278     /**
279      * Helps stripping address part of a CIDR
280      */
281     private static String substringBeforeSlash(String address) {
282         return (address.contains("/") && address.split("/").length > 0) ? address.split("/")[0] : address;
283     }
284
285     static List<GbpAceBuilder> denyDomainSubnets(@Nonnull PolicyContext ctx, @Nonnull ACE_DIRECTION policyDirection) {
286         List<GbpAceBuilder> aclRuleBuilders = new ArrayList<>();
287         for (RendererForwardingByTenant rf : ctx.getPolicy()
288             .getConfiguration()
289             .getRendererForwarding()
290             .getRendererForwardingByTenant()) {
291             rf.getRendererNetworkDomain()
292                 .stream()
293                 .filter(rnd -> org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.Subnet.class
294                     .equals(rnd.getNetworkDomainType()))
295                 .forEach(rnd -> {
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()));
301                     }
302                     else if (subnetAug.getSubnet().isIsTenant()) {
303                         aclRuleBuilders.add(denyEgressTrafficForPrefix(subnetAug.getSubnet()));
304                     }
305                 });
306         }
307         return aclRuleBuilders;
308     }
309
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();
318         }
319         throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
320     }
321
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);
326         } else {
327             rule.setIpAddresses(new Ipv4Prefix(address), null);
328         }
329     }
330
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));
335         } else {
336             rule.setIpAddresses(null, new Ipv4Prefix(address));
337         }
338     }
339
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();
348         }
349         throw new IllegalStateException("Unknown prefix type " + subnet.getIpPrefix());
350     }
351 }