Upgrading ACL implementation
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / policy / acl / AddressMapper.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.UnknownHostException;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.stream.Collectors;
15
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRangeBuilder;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRangeBuilder;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.containment.endpoints.ContainmentEndpoint;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.IpPrefixType;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.L2BridgeDomain;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.L3Context;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev170511.MacAddressType;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocation;
25 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;
26 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;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.common.base.Predicate;
31 import com.google.common.collect.ImmutableMap;
32
33
34 abstract class AddressMapper {
35
36     private static final Logger LOG = LoggerFactory.getLogger(AddressMapper.class);
37
38     private AccessListUtil.ACE_DIRECTION direction;
39
40     private static final PortNumber DHCP_67 = new PortNumber(67);
41     private static final PortNumber DHCP_68 = new PortNumber(68);
42     private static final PortNumber DHCPV6_547 = new PortNumber(547);
43     private static final PortNumber DHCPV6_548 = new PortNumber(548);
44     private static final ImmutableMap<PortNumber, PortNumber> dhcpSockets =
45             ImmutableMap.of(DHCP_67, DHCP_68, DHCP_68, DHCP_67, DHCPV6_548, DHCPV6_547, DHCPV6_547, DHCPV6_548);
46
47     AddressMapper(AccessListUtil.ACE_DIRECTION direction) {
48         this.direction = direction;
49     }
50
51     abstract void updateRule(AddressEndpointWithLocation addrEp, GbpAceBuilder aclRuleBuilder);
52
53     //TODO implement for peers with no location
54     abstract List<GbpAceBuilder> updateExtRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
55             ContainmentEndpoint cEp);
56
57     public boolean updateRules(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
58             AddressEndpointWithLocation peerEp) {
59         filterRulesWithIrrelevantAddresses(rules, localEp, peerEp);
60         for (GbpAceBuilder rule : rules) {
61             LOG.info("Rule: {} Updating rule between {} and {}. Start.", rule, localEp.getAddress(),
62                     peerEp.getAddress());
63             boolean isDhcpRule = dhcpSockets.entrySet().stream().anyMatch(dhcpSocket -> {
64                 boolean srcMatch = isInRange(rule.getSourcePortRangeBuilder(), dhcpSocket.getKey());
65                 boolean dstMatch = isInRange(rule.getDestinationPortRangeBuilder(), dhcpSocket.getValue());
66                 return srcMatch && dstMatch;
67             });
68             if (isDhcpRule) {
69                 if (!inSameSubnet(localEp, peerEp)) {
70                     // do not process rules for DHCPs of other networks
71                     LOG.info("Rule: {} Not updating rules between {} and {}. Returning false.", rule,
72                             localEp.getAddress(), peerEp.getAddress());
73                     return false;
74                 }
75                 // do not update addresses for DHCP traffic
76                 LOG.info("Rule: {} Not updating rule between {} and {}. Continue with next.", rule,
77                         localEp.getAddress(), peerEp.getAddress());
78                 continue;
79             }
80             if (this instanceof SourceMapper) {
81                 LOG.info("Rule: {} Updating rule between {} and {}. SourceMapper.", rule, localEp.getAddress(),
82                         peerEp.getAddress());
83                 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
84                     updateRule(localEp, rule);
85                     continue;
86                 }
87                 updateRule(peerEp, rule);
88             } else if (this instanceof DestinationMapper) {
89                 LOG.info("Rule: {} Updating rule between {} and {}. SourceMapper.", rule, localEp.getAddress(),
90                         peerEp.getAddress());
91                 if (AccessListUtil.ACE_DIRECTION.INGRESS.equals(direction)) {
92                     updateRule(peerEp, rule);
93                     continue;
94                 }
95                 updateRule(localEp, rule);
96             }
97         }
98         LOG.info("Updating rules between {} and {}. Done, returning true.",
99                 localEp.getAddress(), peerEp.getAddress());
100         return true;
101     }
102
103     private void filterRulesWithIrrelevantAddresses(List<GbpAceBuilder> rules, AddressEndpointWithLocation localEp,
104             AddressEndpointWithLocation peerEp) {
105         try {
106             if (!IpPrefixType.class.equals(localEp.getAddressType())
107                     || !IpPrefixType.class.equals(peerEp.getAddressType())) {
108                 return;
109             }
110             boolean addressV4Check = AccessListUtil.isIpv4Address(localEp.getAddress())
111                     && AccessListUtil.isIpv4Address(peerEp.getAddress());
112             boolean addressV6Check = AccessListUtil.isIpv6Address(localEp.getAddress())
113                     && AccessListUtil.isIpv6Address(peerEp.getAddress());
114             Predicate<GbpAceBuilder> p;
115             if (addressV4Check) {
116                 p = rule -> rule.getEtherType().get() instanceof AceIpv6;
117             } else if (addressV6Check) {
118                 p = rule -> rule.getEtherType().get() instanceof AceIpv4;
119             } else {
120                 return;
121             }
122             List<GbpAceBuilder> rulesToRemove = rules.stream()
123                 .filter(rule -> rule.getEtherType().isPresent())
124                 .filter(p)
125                 .collect(Collectors.toList());
126             rulesToRemove.forEach(rule -> { LOG.info("Filtering rules by ethertype {}", rule); rules.remove(rule);});
127         } catch (UnknownHostException e) {
128             LOG.error("Failed to parse addresses {}", e);
129         }
130     }
131
132     private static boolean inSameSubnet(AddressEndpointWithLocation localEp, AddressEndpointWithLocation peerEp) {
133         List<Predicate<AddressEndpointWithLocation>> list = new ArrayList<>();
134         list.add(x -> x != null);
135         list.add(x -> x.getContextType().equals(L3Context.class));
136         list.add(x -> x.getChildEndpoint() != null && !x.getChildEndpoint().isEmpty());
137         list.add(x -> x.getAbsoluteLocation() != null);
138         list.add(x -> x.getChildEndpoint().get(0).getContextType().equals(L2BridgeDomain.class));
139         list.add(x -> x.getChildEndpoint().get(0).getAddressType().equals(MacAddressType.class));
140         if (list.stream().filter(i -> !i.apply(peerEp)).findFirst().isPresent()) {
141             return false;
142         }
143         return localEp.getChildEndpoint().get(0).getContextId().equals(peerEp.getChildEndpoint().get(0).getContextId());
144     }
145
146     private static boolean isInRange(SourcePortRangeBuilder spr, PortNumber portNumber) {
147         return spr != null && isInRange(spr.getLowerPort(), spr.getUpperPort(), portNumber);
148     }
149
150     private static boolean isInRange(DestinationPortRangeBuilder dpr, PortNumber portNumber) {
151         return dpr != null && isInRange(dpr.getLowerPort(),dpr.getUpperPort(), portNumber);
152     }
153
154     private static boolean isInRange(PortNumber lower, PortNumber upper, PortNumber ref) {
155         if (lower != null && upper != null) {
156             return (lower.getValue() <= ref.getValue()) && (ref.getValue() <= upper.getValue());
157         }
158         return (lower != null && lower.getValue().equals(ref.getValue()))
159                 || (upper != null && upper.getValue().equals(ref.getValue()));
160     }
161 }