dbdafa2c8ec210f7dd082b16ff9fdab03b4815d3
[netvirt.git] / neutronvpn / impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronSecurityRuleListener.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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 package org.opendaylight.netvirt.neutronvpn;
9
10 import static org.opendaylight.controller.md.sal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12
13 import com.google.common.collect.ImmutableBiMap;
14
15 import java.util.Collections;
16
17 import javax.annotation.PostConstruct;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
24 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
25 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.AclKey;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceBuilder;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.AceKey;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.ActionsBuilder;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.MatchesBuilder;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.actions.packet.handling.PermitBuilder;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpBuilder;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6Builder;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRangeBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttr;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.SecurityRuleAttrBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.DirectionBase;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.DirectionEgress;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.DirectionIngress;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolBase;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolIcmp;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolIcmpV6;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolTcp;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.constants.rev150712.ProtocolUdp;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.SecurityRuleAttributes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.SecurityRules;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.secgroups.rev150712.security.rules.attributes.security.rules.SecurityRule;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.common.Empty;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 @Singleton
63 public class NeutronSecurityRuleListener
64         extends AsyncDataTreeChangeListenerBase<SecurityRule, NeutronSecurityRuleListener> {
65     private static final Logger LOG = LoggerFactory.getLogger(NeutronSecurityRuleListener.class);
66     private static final ImmutableBiMap<Class<? extends DirectionBase>,
67         Class<?extends org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.rev160608.DirectionBase>>
68         DIRECTION_MAP = ImmutableBiMap.of(
69             DirectionEgress.class, NeutronSecurityGroupConstants.DIRECTION_EGRESS,
70             DirectionIngress.class, NeutronSecurityGroupConstants.DIRECTION_INGRESS);
71     private static final ImmutableBiMap<Class<? extends ProtocolBase>, Short> PROTOCOL_MAP = ImmutableBiMap.of(
72             ProtocolIcmp.class, NeutronSecurityGroupConstants.PROTOCOL_ICMP,
73             ProtocolTcp.class, NeutronSecurityGroupConstants.PROTOCOL_TCP,
74             ProtocolUdp.class, NeutronSecurityGroupConstants.PROTOCOL_UDP,
75             ProtocolIcmpV6.class, NeutronSecurityGroupConstants.PROTOCOL_ICMPV6);
76
77     private final DataBroker dataBroker;
78     private final ManagedNewTransactionRunner txRunner;
79     private final JobCoordinator jobCoordinator;
80
81     @Inject
82     public NeutronSecurityRuleListener(final DataBroker dataBroker, JobCoordinator jobCoordinator) {
83         super(SecurityRule.class, NeutronSecurityRuleListener.class);
84         this.dataBroker = dataBroker;
85         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
86         this.jobCoordinator = jobCoordinator;
87     }
88
89     @Override
90     @PostConstruct
91     public void init() {
92         LOG.info("{} init", getClass().getSimpleName());
93         registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
94     }
95
96     @Override
97     protected InstanceIdentifier<SecurityRule> getWildCardPath() {
98         return InstanceIdentifier.create(Neutron.class).child(SecurityRules.class).child(SecurityRule.class);
99     }
100
101     @Override
102     // TODO Clean up the exception handling
103     @SuppressWarnings("checkstyle:IllegalCatch")
104     protected void add(InstanceIdentifier<SecurityRule> instanceIdentifier, SecurityRule securityRule) {
105         LOG.trace("added securityRule: {}", securityRule);
106         try {
107             Ace ace = toAceBuilder(securityRule, false).build();
108             InstanceIdentifier<Ace> identifier = getAceInstanceIdentifier(securityRule);
109             String jobKey = securityRule.getSecurityGroupId().getValue();
110             jobCoordinator.enqueueJob(jobKey,
111                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
112                     tx -> tx.put(identifier, ace, CREATE_MISSING_PARENTS))),
113                     NeutronSecurityGroupConstants.DJC_MAX_RETRIES);
114         } catch (Exception ex) {
115             LOG.error("Exception occured while adding acl for security rule: {}. ", securityRule, ex);
116         }
117     }
118
119     private InstanceIdentifier<Ace> getAceInstanceIdentifier(SecurityRule securityRule) {
120         return InstanceIdentifier
121                 .builder(AccessLists.class)
122                 .child(Acl.class,
123                         new AclKey(securityRule.getSecurityGroupId().getValue(), NeutronSecurityGroupConstants.ACLTYPE))
124                 .child(AccessListEntries.class)
125                 .child(Ace.class,
126                         new AceKey(securityRule.getUuid().getValue()))
127                 .build();
128     }
129
130     private AceBuilder toAceBuilder(SecurityRule securityRule, boolean isDeleted) {
131         AceIpBuilder aceIpBuilder = new AceIpBuilder();
132         SecurityRuleAttrBuilder securityRuleAttrBuilder = new SecurityRuleAttrBuilder();
133         DestinationPortRangeBuilder destinationPortRangeBuilder = new DestinationPortRangeBuilder();
134         boolean isDirectionIngress = false;
135         if (securityRule.getDirection() != null) {
136             securityRuleAttrBuilder.setDirection(DIRECTION_MAP.get(securityRule.getDirection()));
137             isDirectionIngress = securityRule.getDirection().equals(DirectionIngress.class);
138         }
139         if (securityRule.getPortRangeMax() != null) {
140             destinationPortRangeBuilder.setUpperPort(new PortNumber(securityRule.getPortRangeMax()));
141
142         }
143         if (securityRule.getPortRangeMin() != null) {
144             destinationPortRangeBuilder.setLowerPort(new PortNumber(securityRule.getPortRangeMin()));
145             // set destination port range if lower port is specified as it is mandatory parameter in acl model
146             aceIpBuilder.setDestinationPortRange(destinationPortRangeBuilder.build());
147         }
148         aceIpBuilder = handleRemoteIpPrefix(securityRule, aceIpBuilder, isDirectionIngress);
149         if (securityRule.getRemoteGroupId() != null) {
150             securityRuleAttrBuilder.setRemoteGroupId(securityRule.getRemoteGroupId());
151         }
152         if (securityRule.getProtocol() != null) {
153             SecurityRuleAttributes.Protocol protocol = securityRule.getProtocol();
154             if (protocol.getUint8() != null) {
155                 // uint8
156                 aceIpBuilder.setProtocol(protocol.getUint8());
157             } else {
158                 // symbolic protocol name
159                 aceIpBuilder.setProtocol(PROTOCOL_MAP.get(protocol.getIdentityref()));
160             }
161         }
162         securityRuleAttrBuilder.setDeleted(isDeleted);
163
164         MatchesBuilder matchesBuilder = new MatchesBuilder();
165         matchesBuilder.setAceType(aceIpBuilder.build());
166         // set acl action as permit for the security rule
167         ActionsBuilder actionsBuilder = new ActionsBuilder();
168         actionsBuilder.setPacketHandling(new PermitBuilder().setPermit(Empty.getInstance()).build());
169
170         AceBuilder aceBuilder = new AceBuilder();
171         aceBuilder.withKey(new AceKey(securityRule.getUuid().getValue()));
172         aceBuilder.setRuleName(securityRule.getUuid().getValue());
173         aceBuilder.setMatches(matchesBuilder.build());
174         aceBuilder.setActions(actionsBuilder.build());
175         aceBuilder.addAugmentation(SecurityRuleAttr.class, securityRuleAttrBuilder.build());
176         return aceBuilder;
177     }
178
179     private AceIpBuilder handleEtherType(SecurityRule securityRule, AceIpBuilder aceIpBuilder) {
180         if (NeutronSecurityGroupConstants.ETHERTYPE_IPV4.equals(securityRule.getEthertype())) {
181             AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
182             aceIpv4Builder.setSourceIpv4Network(new Ipv4Prefix(
183                 NeutronSecurityGroupConstants.IPV4_ALL_NETWORK));
184             aceIpv4Builder.setDestinationIpv4Network(new Ipv4Prefix(
185                 NeutronSecurityGroupConstants.IPV4_ALL_NETWORK));
186             aceIpBuilder.setAceIpVersion(aceIpv4Builder.build());
187         } else {
188             AceIpv6Builder aceIpv6Builder = new AceIpv6Builder();
189             aceIpv6Builder.setSourceIpv6Network(new Ipv6Prefix(
190                 NeutronSecurityGroupConstants.IPV6_ALL_NETWORK));
191             aceIpv6Builder.setDestinationIpv6Network(new Ipv6Prefix(
192                 NeutronSecurityGroupConstants.IPV6_ALL_NETWORK));
193             aceIpBuilder.setAceIpVersion(aceIpv6Builder.build());
194
195         }
196         return aceIpBuilder;
197     }
198
199     private AceIpBuilder handleRemoteIpPrefix(SecurityRule securityRule, AceIpBuilder aceIpBuilder,
200                                               boolean isDirectionIngress) {
201         if (securityRule.getRemoteIpPrefix() != null) {
202             if (securityRule.getRemoteIpPrefix().getIpv4Prefix() != null) {
203                 AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
204                 if (isDirectionIngress) {
205                     aceIpv4Builder.setSourceIpv4Network(new Ipv4Prefix(securityRule
206                         .getRemoteIpPrefix().getIpv4Prefix().getValue()));
207                 } else {
208                     aceIpv4Builder.setDestinationIpv4Network(new Ipv4Prefix(securityRule
209                         .getRemoteIpPrefix().getIpv4Prefix().getValue()));
210                 }
211                 aceIpBuilder.setAceIpVersion(aceIpv4Builder.build());
212             } else {
213                 AceIpv6Builder aceIpv6Builder = new AceIpv6Builder();
214                 if (isDirectionIngress) {
215                     aceIpv6Builder.setSourceIpv6Network(new Ipv6Prefix(
216                         securityRule.getRemoteIpPrefix().getIpv6Prefix().getValue()));
217                 } else {
218                     aceIpv6Builder.setDestinationIpv6Network(new Ipv6Prefix(
219                         securityRule.getRemoteIpPrefix().getIpv6Prefix().getValue()));
220                 }
221                 aceIpBuilder.setAceIpVersion(aceIpv6Builder.build());
222             }
223         } else {
224             if (securityRule.getEthertype() != null) {
225                 handleEtherType(securityRule, aceIpBuilder);
226             }
227         }
228
229         return aceIpBuilder;
230     }
231
232     @Override
233     // TODO Clean up the exception handling
234     @SuppressWarnings("checkstyle:IllegalCatch")
235     protected void remove(InstanceIdentifier<SecurityRule> instanceIdentifier, SecurityRule securityRule) {
236         LOG.trace("removed securityRule: {}", securityRule);
237         InstanceIdentifier<Ace> identifier = getAceInstanceIdentifier(securityRule);
238         try {
239             Ace ace = toAceBuilder(securityRule, true).build();
240             String jobKey = securityRule.getSecurityGroupId().getValue();
241             jobCoordinator.enqueueJob(jobKey,
242                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
243                     tx -> tx.merge(identifier, ace, CREATE_MISSING_PARENTS))),
244                     NeutronSecurityGroupConstants.DJC_MAX_RETRIES);
245         } catch (Exception ex) {
246             /*
247             If there are out of sequence events where-in Sg-Rule delete could occur after SecurityGroup delete.
248             we would hit with exception here, trying to delete a child-node, after it's parent has got deleted.
249             Logging it as Warn, because if such event occur we are handling it in the AclEventListeners' Remove method.
250              */
251             LOG.warn("Exception occured while removing acl for security rule: {}. ", securityRule, ex);
252         }
253     }
254
255     @Override
256     protected void update(InstanceIdentifier<SecurityRule> instanceIdentifier,
257                           SecurityRule oldSecurityRule, SecurityRule updatedSecurityRule) {
258         // security rule updation is not supported from openstack, so no need to handle update.
259         LOG.trace("updates on security rules not supported.");
260     }
261
262     @Override
263     protected NeutronSecurityRuleListener getDataTreeChangeListener() {
264         return this;
265     }
266 }