Changing the conntrack default drop and allow flows from per port to per
[netvirt.git] / vpnservice / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / listeners / AclNodeListener.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
9 package org.opendaylight.netvirt.aclservice.listeners;
10
11 import com.google.common.base.Optional;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.List;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.ActionType;
20 import org.opendaylight.genius.mdsalutil.FlowEntity;
21 import org.opendaylight.genius.mdsalutil.InstructionInfo;
22 import org.opendaylight.genius.mdsalutil.InstructionType;
23 import org.opendaylight.genius.mdsalutil.MDSALDataStoreUtils;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchFieldType;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
28 import org.opendaylight.genius.mdsalutil.NwConstants;
29 import org.opendaylight.genius.mdsalutil.NxMatchFieldType;
30 import org.opendaylight.genius.mdsalutil.NxMatchInfo;
31 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
32 import org.opendaylight.netvirt.aclservice.utils.AclConstants;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.config.rev160806.AclserviceConfig.SecurityGroupMode;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.aclservice.config.rev160806.AclserviceConfig;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 /**
44  * Listener to handle flow capable node updates.
45  */
46 @SuppressWarnings("deprecation")
47 public class AclNodeListener extends AsyncDataTreeChangeListenerBase<FlowCapableNode, AclNodeListener>
48         implements AutoCloseable {
49
50     /** The Constant LOG. */
51     private static final Logger LOG = LoggerFactory.getLogger(AclNodeListener.class);
52
53     /** The mdsal manager. */
54     private final IMdsalApiManager mdsalManager;
55
56     /** The data broker. */
57     private final DataBroker dataBroker;
58
59     private SecurityGroupMode securityGroupMode = null;
60
61     /**
62      * Instantiates a new acl node listener.
63      *
64      * @param mdsalManager the mdsal manager
65      */
66     public AclNodeListener(final IMdsalApiManager mdsalManager, DataBroker dataBroker) {
67         super(FlowCapableNode.class, AclNodeListener.class);
68
69         this.mdsalManager = mdsalManager;
70         this.dataBroker = dataBroker;
71     }
72
73     public void start() {
74         LOG.info("{} start", getClass().getSimpleName());
75         Optional<AclserviceConfig> aclConfig = MDSALDataStoreUtils.read(dataBroker,
76                 LogicalDatastoreType.CONFIGURATION, InstanceIdentifier
77                 .create(AclserviceConfig.class));
78         if (aclConfig.isPresent()) {
79             this.securityGroupMode = aclConfig.get().getSecurityGroupMode();
80         }
81         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
82     }
83
84     /*
85      * (non-Javadoc)
86      *
87      * @see
88      * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
89      * getWildCardPath()
90      */
91     @Override
92     protected InstanceIdentifier<FlowCapableNode> getWildCardPath() {
93         return InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
94     }
95
96     /*
97      * (non-Javadoc)
98      *
99      * @see
100      * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
101      * remove(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
102      * org.opendaylight.yangtools.yang.binding.DataObject)
103      */
104     @Override
105     protected void remove(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModification) {
106         // do nothing
107
108     }
109
110     /*
111      * (non-Javadoc)
112      *
113      * @see
114      * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
115      * update(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
116      * org.opendaylight.yangtools.yang.binding.DataObject,
117      * org.opendaylight.yangtools.yang.binding.DataObject)
118      */
119     @Override
120     protected void update(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModificationBefore,
121             FlowCapableNode dataObjectModificationAfter) {
122         // do nothing
123
124     }
125
126     /*
127      * (non-Javadoc)
128      *
129      * @see
130      * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
131      * add(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
132      * org.opendaylight.yangtools.yang.binding.DataObject)
133      */
134     @Override
135     protected void add(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModification) {
136         LOG.trace("FlowCapableNode Added: key: {}", key);
137
138         NodeKey nodeKey = key.firstKeyOf(Node.class);
139         BigInteger dpnId = MDSALUtil.getDpnIdFromNodeName(nodeKey.getId());
140         createTableDefaultEntries(dpnId);
141     }
142
143     /**
144      * Creates the table miss entries.
145      *
146      * @param dpnId the dpn id
147      */
148     private void createTableDefaultEntries(BigInteger dpnId) {
149         if (securityGroupMode == null || securityGroupMode == SecurityGroupMode.Stateful) {
150             addIngressAclTableMissFlow(dpnId);
151             addEgressAclTableMissFlow(dpnId);
152             addConntrackRules(dpnId, NwConstants.LPORT_DISPATCHER_TABLE,
153                 NwConstants.EGRESS_ACL_NEXT_TABLE_ID, NwConstants.ADD_FLOW);
154             addConntrackRules(dpnId,NwConstants.EGRESS_LPORT_DISPATCHER_TABLE,
155                 NwConstants.INGRESS_ACL_NEXT_TABLE_ID,
156                  NwConstants.ADD_FLOW);
157         } else {
158             addStatelessIngressAclTableMissFlow(dpnId);
159             addStatelessEgressAclTableMissFlow(dpnId);
160         }
161     }
162
163     /**
164      * Adds the ingress acl table miss flow.
165      *
166      * @param dpId the dp id
167      */
168     private void addIngressAclTableMissFlow(BigInteger dpId) {
169         List<MatchInfo> mkMatches = new ArrayList<>();
170         List<InstructionInfo> mkInstructions = new ArrayList<>();
171         List<ActionInfo> actionsInfos = new ArrayList<>();
172         actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
173         mkInstructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
174
175         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE_ID,
176                 getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE_ID), 0, "Ingress ACL Table Miss Flow", 0, 0,
177                 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
178         mdsalManager.installFlow(flowEntity);
179
180         FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_NEXT_TABLE_ID,
181                 getTableMissFlowId(NwConstants.INGRESS_ACL_NEXT_TABLE_ID), 0, "Ingress ACL Filter Table Miss Flow",
182                 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
183         mdsalManager.installFlow(nextTblFlowEntity);
184
185         LOG.debug("Added Ingress ACL Table Miss Flows for dpn {}", dpId);
186     }
187
188     /**
189      * Adds the ingress acl table miss flow.
190      *
191      * @param dpId the dp id
192      */
193     private void addStatelessIngressAclTableMissFlow(BigInteger dpId) {
194         List<InstructionInfo> synInstructions = new ArrayList<>();
195         List<MatchInfo> synMatches = new ArrayList<>();
196         synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN }));
197
198         List<ActionInfo> dropActionsInfos = new ArrayList<>();
199         dropActionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
200         synInstructions.add(new InstructionInfo(InstructionType.apply_actions, dropActionsInfos));
201
202         FlowEntity synFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE_ID,
203                 "SYN-" + getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE_ID),
204                 AclConstants.PROTO_MATCH_SYN_DROP_PRIORITY, "Ingress Syn ACL Table Block", 0, 0,
205                 AclConstants.COOKIE_ACL_BASE, synMatches, synInstructions);
206         mdsalManager.installFlow(synFlowEntity);
207
208         synMatches = new ArrayList<>();
209         synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
210
211         List<InstructionInfo> allowAllInstructions = new ArrayList<>();
212         allowAllInstructions.add(
213             new InstructionInfo(InstructionType.goto_table,
214                     new long[] { NwConstants.INGRESS_ACL_NEXT_TABLE_ID }));
215
216         FlowEntity synAckFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE_ID,
217                 "SYN-ACK-ALLOW-" + getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE_ID),
218                 AclConstants.PROTO_MATCH_SYN_ACK_ALLOW_PRIORITY, "Ingress Syn Ack ACL Table Allow", 0, 0,
219                 AclConstants.COOKIE_ACL_BASE, synMatches, allowAllInstructions);
220         mdsalManager.installFlow(synAckFlowEntity);
221
222
223         List<MatchInfo> mkMatches = new ArrayList<>();
224         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE_ID,
225                 getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE_ID), 0, "Ingress Stateless ACL Table Miss Flow",
226                 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches, allowAllInstructions);
227         mdsalManager.installFlow(flowEntity);
228
229         FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_NEXT_TABLE_ID,
230                 getTableMissFlowId(NwConstants.INGRESS_ACL_NEXT_TABLE_ID), 0,
231                 "Ingress Stateless Next ACL Table Miss Flow", 0, 0, AclConstants.COOKIE_ACL_BASE,
232                 mkMatches, allowAllInstructions);
233         mdsalManager.installFlow(nextTblFlowEntity);
234
235         LOG.debug("Added Stateless Ingress ACL Table Miss Flows for dpn {}", dpId);
236     }
237
238     /**
239      * Adds the stateless egress acl table miss flow.
240      *
241      * @param dpId the dp id
242      */
243     private void addStatelessEgressAclTableMissFlow(BigInteger dpId) {
244         List<InstructionInfo> allowAllInstructions = new ArrayList<>();
245         allowAllInstructions.add(
246                 new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.EGRESS_ACL_NEXT_TABLE_ID }));
247
248         List<InstructionInfo> synInstructions = new ArrayList<>();
249         List<MatchInfo> synMatches = new ArrayList<>();
250         synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN }));
251
252         List<ActionInfo> synActionsInfos = new ArrayList<>();
253         synActionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
254         synInstructions.add(new InstructionInfo(InstructionType.apply_actions, synActionsInfos));
255
256         FlowEntity synFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE_ID,
257                 "SYN-" + getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE_ID),
258                 AclConstants.PROTO_MATCH_SYN_DROP_PRIORITY, "Egress Syn ACL Table Block", 0, 0,
259                 AclConstants.COOKIE_ACL_BASE, synMatches, synInstructions);
260         mdsalManager.installFlow(synFlowEntity);
261
262         synMatches = new ArrayList<>();
263         synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
264
265         FlowEntity synAckFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE_ID,
266                 "SYN-ACK-ALLOW-" + getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE_ID),
267                 AclConstants.PROTO_MATCH_SYN_ACK_ALLOW_PRIORITY, "Egress Syn Ack ACL Table Allow", 0, 0,
268                 AclConstants.COOKIE_ACL_BASE, synMatches, allowAllInstructions);
269         mdsalManager.installFlow(synAckFlowEntity);
270
271         List<MatchInfo> mkMatches = new ArrayList<>();
272         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE_ID,
273                 getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE_ID), 0, "Egress Stateless ACL Table Miss Flow", 0, 0,
274                 AclConstants.COOKIE_ACL_BASE, mkMatches, allowAllInstructions);
275         mdsalManager.installFlow(flowEntity);
276
277         FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_NEXT_TABLE_ID,
278                 getTableMissFlowId(NwConstants.EGRESS_ACL_NEXT_TABLE_ID), 0,
279                 "Egress Stateless Next ACL Table Miss Flow", 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches,
280                 allowAllInstructions);
281         mdsalManager.installFlow(nextTblFlowEntity);
282
283         LOG.debug("Added Stateless Egress ACL Table Miss Flows for dpn {}", dpId);
284     }
285
286     /**
287      * Adds the egress acl table miss flow.
288      *
289      * @param dpId the dp id
290      */
291     private void addEgressAclTableMissFlow(BigInteger dpId) {
292         List<MatchInfo> mkMatches = new ArrayList<>();
293         List<InstructionInfo> mkInstructions = new ArrayList<>();
294         List<ActionInfo> actionsInfos = new ArrayList<>();
295         actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
296         mkInstructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
297
298         FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE_ID,
299                 getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE_ID), 0, "Egress ACL Table Miss Flow", 0, 0,
300                 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
301         mdsalManager.installFlow(flowEntity);
302
303         FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_NEXT_TABLE_ID,
304                 getTableMissFlowId(NwConstants.EGRESS_ACL_NEXT_TABLE_ID), 0, "Egress ACL Table Miss Flow", 0, 0,
305                 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
306         mdsalManager.installFlow(nextTblFlowEntity);
307
308         LOG.debug("Added Egress ACL Table Miss Flows for dpn {}", dpId);
309     }
310
311     private void addConntrackRules(BigInteger dpnId, short dispatcherTableId,short tableId, int write) {
312         programConntrackForwardRule(dpnId, AclConstants.CT_STATE_TRACKED_EXIST_PRIORITY,
313             "Tracked_Established", AclConstants.TRACKED_EST_CT_STATE, AclConstants.TRACKED_EST_CT_STATE_MASK,
314             dispatcherTableId, tableId, write );
315         programConntrackForwardRule(dpnId, AclConstants.CT_STATE_TRACKED_EXIST_PRIORITY,"Tracked_Related", AclConstants
316             .TRACKED_REL_CT_STATE, AclConstants.TRACKED_REL_CT_STATE_MASK, dispatcherTableId, tableId, write );
317         programConntrackDropRule(dpnId, AclConstants.CT_STATE_NEW_PRIORITY_DROP,"Tracked_New",
318             AclConstants.TRACKED_NEW_CT_STATE, AclConstants.TRACKED_NEW_CT_STATE_MASK, tableId, write );
319         programConntrackDropRule(dpnId, AclConstants.CT_STATE_NEW_PRIORITY_DROP, "Tracked_Invalid",
320             AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, tableId, write );
321
322     }
323
324     /**
325      * Adds the rule to forward the packets known packets.
326      *
327      * @param dpId the dpId
328      * @param lportTag the lport tag
329      * @param priority the priority of the flow
330      * @param flowId the flowId
331      * @param conntrackState the conntrack state of the packets thats should be
332      *        send
333      * @param conntrackMask the conntrack mask
334      * @param addOrRemove whether to add or remove the flow
335      */
336     private void programConntrackForwardRule(BigInteger dpId, Integer priority, String flowId,
337             int conntrackState, int conntrackMask, short dispatcherTableId, short tableId, int addOrRemove) {
338         List<MatchInfoBase> matches = new ArrayList<>();
339         matches.add(new NxMatchInfo(NxMatchFieldType.ct_state, new long[] {conntrackState, conntrackMask}));
340
341         List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(
342             new ArrayList<>(),dispatcherTableId);
343
344         flowId = "Fixed_Conntrk_Trk_" + dpId + "_" + flowId + dispatcherTableId;
345         syncFlow(dpId, tableId, flowId, priority, "ACL", 0, 0,
346                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
347     }
348
349     /**
350      * Adds the rule to drop the unknown/invalid packets .
351      *
352      * @param dpId the dpId
353      * @param lportTag the lport tag
354      * @param priority the priority of the flow
355      * @param flowId the flowId
356      * @param conntrackState the conntrack state of the packets thats should be
357      *        send
358      * @param conntrackMask the conntrack mask
359      * @param addOrRemove whether to add or remove the flow
360      */
361     private void programConntrackDropRule(BigInteger dpId, Integer priority, String flowId,
362             int conntrackState, int conntrackMask, short tableId, int addOrRemove) {
363         List<MatchInfoBase> matches = new ArrayList<>();
364         matches.add(new NxMatchInfo(NxMatchFieldType.ct_state, new long[] {conntrackState, conntrackMask}));
365
366         List<InstructionInfo> instructions = new ArrayList<>();
367         List<ActionInfo> actionsInfos = new ArrayList<>();
368         actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
369         instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
370         flowId = "Fixed_Conntrk_NewDrop_" + dpId + "_" + flowId + tableId;
371         syncFlow(dpId, tableId, flowId, priority, "ACL", 0, 0,
372                 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
373     }
374
375     /**
376      * Gets the dispatcher table resubmit instructions.
377      *
378      * @param actionsInfos the actions infos
379      * @return the instructions for dispatcher table resubmit
380      */
381     private List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos,
382                                                                          short dispatcherTableId) {
383         List<InstructionInfo> instructions = new ArrayList<>();
384         actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] {Short.toString(dispatcherTableId)}));
385         instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
386         return instructions;
387     }
388
389     /**
390      * Writes/remove the flow to/from the datastore.
391      * @param dpId the dpId
392      * @param tableId the tableId
393      * @param flowId the flowId
394      * @param priority the priority
395      * @param flowName the flow name
396      * @param idleTimeOut the idle timeout
397      * @param hardTimeOut the hard timeout
398      * @param cookie the cookie
399      * @param matches the list of matches to be written
400      * @param instructions the list of instruction to be written.
401      * @param addOrRemove add or remove the entries.
402      */
403     protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
404                           int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase>  matches,
405                           List<InstructionInfo> instructions, int addOrRemove) {
406         if (addOrRemove == NwConstants.DEL_FLOW) {
407             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,flowId,
408                 priority, flowName , idleTimeOut, hardTimeOut, cookie, matches, null);
409             LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
410             mdsalManager.removeFlow(flowEntity);
411         } else {
412             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId,
413                 priority, flowName, idleTimeOut, hardTimeOut, cookie, matches, instructions);
414             LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
415             mdsalManager.installFlow(flowEntity);
416         }
417     }
418
419     /**
420      * Gets the table miss flow id.
421      *
422      * @param tableId the table id
423      * @return the table miss flow id
424      */
425     private String getTableMissFlowId(short tableId) {
426         return String.valueOf(tableId);
427     }
428
429     /*
430      * (non-Javadoc)
431      *
432      * @see
433      * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
434      * getDataTreeChangeListener()
435      */
436     @Override
437     protected AclNodeListener getDataTreeChangeListener() {
438         return AclNodeListener.this;
439     }
440 }