Upgrading ACL implementation
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / policy / acl / AddressMapper.java
index 8d42237db05b4f0a37186af7e3842c9129a2b521..f84e05fceb912eccf9d18a43f2db95d06c0b85d3 100644 (file)
@@ -8,23 +8,41 @@
 
 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;
@@ -36,55 +54,104 @@ abstract class AddressMapper {
     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());
         }