2 * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.aclservice.listeners;
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;
44 * Listener to handle flow capable node updates.
46 @SuppressWarnings("deprecation")
47 public class AclNodeListener extends AsyncDataTreeChangeListenerBase<FlowCapableNode, AclNodeListener>
48 implements AutoCloseable {
50 /** The Constant LOG. */
51 private static final Logger LOG = LoggerFactory.getLogger(AclNodeListener.class);
53 /** The mdsal manager. */
54 private final IMdsalApiManager mdsalManager;
56 /** The data broker. */
57 private final DataBroker dataBroker;
59 private SecurityGroupMode securityGroupMode = null;
62 * Instantiates a new acl node listener.
64 * @param mdsalManager the mdsal manager
66 public AclNodeListener(final IMdsalApiManager mdsalManager, DataBroker dataBroker) {
67 super(FlowCapableNode.class, AclNodeListener.class);
69 this.mdsalManager = mdsalManager;
70 this.dataBroker = dataBroker;
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();
81 registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
88 * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
92 protected InstanceIdentifier<FlowCapableNode> getWildCardPath() {
93 return InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
100 * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
101 * remove(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
102 * org.opendaylight.yangtools.yang.binding.DataObject)
105 protected void remove(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModification) {
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)
120 protected void update(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModificationBefore,
121 FlowCapableNode dataObjectModificationAfter) {
130 * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
131 * add(org.opendaylight.yangtools.yang.binding.InstanceIdentifier,
132 * org.opendaylight.yangtools.yang.binding.DataObject)
135 protected void add(InstanceIdentifier<FlowCapableNode> key, FlowCapableNode dataObjectModification) {
136 LOG.trace("FlowCapableNode Added: key: {}", key);
138 NodeKey nodeKey = key.firstKeyOf(Node.class);
139 BigInteger dpnId = MDSALUtil.getDpnIdFromNodeName(nodeKey.getId());
140 createTableDefaultEntries(dpnId);
144 * Creates the table miss entries.
146 * @param dpnId the dpn id
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, NwConstants.INGRESS_ACL_FILTER_TABLE,
153 NwConstants.ADD_FLOW);
154 addConntrackRules(dpnId, NwConstants.EGRESS_LPORT_DISPATCHER_TABLE, NwConstants.EGRESS_ACL_FILTER_TABLE,
155 NwConstants.ADD_FLOW);
157 addStatelessIngressAclTableMissFlow(dpnId);
158 addStatelessEgressAclTableMissFlow(dpnId);
163 * Adds the ingress acl table miss flow.
165 * @param dpId the dp id
167 private void addIngressAclTableMissFlow(BigInteger dpId) {
168 List<MatchInfo> mkMatches = new ArrayList<>();
169 List<InstructionInfo> mkInstructions = new ArrayList<>();
170 List<ActionInfo> actionsInfos = new ArrayList<>();
171 actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
172 mkInstructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
174 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE,
175 getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE), 0, "Ingress ACL Table Miss Flow", 0, 0,
176 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
177 mdsalManager.installFlow(flowEntity);
179 FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_FILTER_TABLE,
180 getTableMissFlowId(NwConstants.EGRESS_ACL_FILTER_TABLE), 0, "Ingress ACL Filter Table Miss Flow",
181 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
182 mdsalManager.installFlow(nextTblFlowEntity);
184 LOG.debug("Added Ingress ACL Table Miss Flows for dpn {}", dpId);
188 * Adds the ingress acl table miss flow.
190 * @param dpId the dp id
192 private void addStatelessIngressAclTableMissFlow(BigInteger dpId) {
193 List<InstructionInfo> synInstructions = new ArrayList<>();
194 List<MatchInfo> synMatches = new ArrayList<>();
195 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN }));
197 List<ActionInfo> dropActionsInfos = new ArrayList<>();
198 dropActionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
199 synInstructions.add(new InstructionInfo(InstructionType.apply_actions, dropActionsInfos));
201 FlowEntity synFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE,
202 "SYN-" + getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE),
203 AclConstants.PROTO_MATCH_SYN_DROP_PRIORITY, "Ingress Syn ACL Table Block", 0, 0,
204 AclConstants.COOKIE_ACL_BASE, synMatches, synInstructions);
205 mdsalManager.installFlow(synFlowEntity);
207 synMatches = new ArrayList<>();
208 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
210 List<InstructionInfo> allowAllInstructions = new ArrayList<>();
211 allowAllInstructions.add(
212 new InstructionInfo(InstructionType.goto_table,
213 new long[] { NwConstants.EGRESS_ACL_FILTER_TABLE }));
215 FlowEntity synAckFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE,
216 "SYN-ACK-ALLOW-" + getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE),
217 AclConstants.PROTO_MATCH_SYN_ACK_ALLOW_PRIORITY, "Ingress Syn Ack ACL Table Allow", 0, 0,
218 AclConstants.COOKIE_ACL_BASE, synMatches, allowAllInstructions);
219 mdsalManager.installFlow(synAckFlowEntity);
222 List<MatchInfo> mkMatches = new ArrayList<>();
223 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_TABLE,
224 getTableMissFlowId(NwConstants.EGRESS_ACL_TABLE), 0, "Ingress Stateless ACL Table Miss Flow",
225 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches, allowAllInstructions);
226 mdsalManager.installFlow(flowEntity);
228 FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.EGRESS_ACL_FILTER_TABLE,
229 getTableMissFlowId(NwConstants.EGRESS_ACL_FILTER_TABLE), 0,
230 "Ingress Stateless Next ACL Table Miss Flow", 0, 0, AclConstants.COOKIE_ACL_BASE,
231 mkMatches, allowAllInstructions);
232 mdsalManager.installFlow(nextTblFlowEntity);
234 LOG.debug("Added Stateless Ingress ACL Table Miss Flows for dpn {}", dpId);
238 * Adds the stateless egress acl table miss flow.
240 * @param dpId the dp id
242 private void addStatelessEgressAclTableMissFlow(BigInteger dpId) {
243 List<InstructionInfo> allowAllInstructions = new ArrayList<>();
244 allowAllInstructions.add(
245 new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.INGRESS_ACL_FILTER_TABLE }));
247 List<InstructionInfo> synInstructions = new ArrayList<>();
248 List<MatchInfo> synMatches = new ArrayList<>();
249 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN }));
251 List<ActionInfo> synActionsInfos = new ArrayList<>();
252 synActionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
253 synInstructions.add(new InstructionInfo(InstructionType.apply_actions, synActionsInfos));
255 FlowEntity synFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE,
256 "SYN-" + getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE),
257 AclConstants.PROTO_MATCH_SYN_DROP_PRIORITY, "Egress Syn ACL Table Block", 0, 0,
258 AclConstants.COOKIE_ACL_BASE, synMatches, synInstructions);
259 mdsalManager.installFlow(synFlowEntity);
261 synMatches = new ArrayList<>();
262 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
264 FlowEntity synAckFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE,
265 "SYN-ACK-ALLOW-" + getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE),
266 AclConstants.PROTO_MATCH_SYN_ACK_ALLOW_PRIORITY, "Egress Syn Ack ACL Table Allow", 0, 0,
267 AclConstants.COOKIE_ACL_BASE, synMatches, allowAllInstructions);
268 mdsalManager.installFlow(synAckFlowEntity);
270 List<MatchInfo> mkMatches = new ArrayList<>();
271 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE,
272 getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE), 0, "Egress Stateless ACL Table Miss Flow", 0, 0,
273 AclConstants.COOKIE_ACL_BASE, mkMatches, allowAllInstructions);
274 mdsalManager.installFlow(flowEntity);
276 FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_FILTER_TABLE,
277 getTableMissFlowId(NwConstants.INGRESS_ACL_FILTER_TABLE), 0,
278 "Egress Stateless Next ACL Table Miss Flow", 0, 0, AclConstants.COOKIE_ACL_BASE, mkMatches,
279 allowAllInstructions);
280 mdsalManager.installFlow(nextTblFlowEntity);
282 LOG.debug("Added Stateless Egress ACL Table Miss Flows for dpn {}", dpId);
286 * Adds the egress acl table miss flow.
288 * @param dpId the dp id
290 private void addEgressAclTableMissFlow(BigInteger dpId) {
291 List<MatchInfo> mkMatches = new ArrayList<>();
292 List<InstructionInfo> mkInstructions = new ArrayList<>();
293 List<ActionInfo> actionsInfos = new ArrayList<>();
294 actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
295 mkInstructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
297 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_TABLE,
298 getTableMissFlowId(NwConstants.INGRESS_ACL_TABLE), 0, "Egress ACL Table Miss Flow", 0, 0,
299 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
300 mdsalManager.installFlow(flowEntity);
302 FlowEntity nextTblFlowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.INGRESS_ACL_FILTER_TABLE,
303 getTableMissFlowId(NwConstants.INGRESS_ACL_FILTER_TABLE), 0, "Egress ACL Table Miss Flow", 0, 0,
304 AclConstants.COOKIE_ACL_BASE, mkMatches, mkInstructions);
305 mdsalManager.installFlow(nextTblFlowEntity);
307 LOG.debug("Added Egress ACL Table Miss Flows for dpn {}", dpId);
310 private void addConntrackRules(BigInteger dpnId, short dispatcherTableId,short tableId, int write) {
311 programConntrackForwardRule(dpnId, AclConstants.CT_STATE_TRACKED_EXIST_PRIORITY,
312 "Tracked_Established", AclConstants.TRACKED_EST_CT_STATE, AclConstants.TRACKED_EST_CT_STATE_MASK,
313 dispatcherTableId, tableId, write );
314 programConntrackForwardRule(dpnId, AclConstants.CT_STATE_TRACKED_EXIST_PRIORITY,"Tracked_Related", AclConstants
315 .TRACKED_REL_CT_STATE, AclConstants.TRACKED_REL_CT_STATE_MASK, dispatcherTableId, tableId, write );
316 programConntrackDropRule(dpnId, AclConstants.CT_STATE_NEW_PRIORITY_DROP,"Tracked_New",
317 AclConstants.TRACKED_NEW_CT_STATE, AclConstants.TRACKED_NEW_CT_STATE_MASK, tableId, write );
318 programConntrackDropRule(dpnId, AclConstants.CT_STATE_NEW_PRIORITY_DROP, "Tracked_Invalid",
319 AclConstants.TRACKED_INV_CT_STATE, AclConstants.TRACKED_INV_CT_STATE_MASK, tableId, write );
324 * Adds the rule to forward the packets known packets.
326 * @param dpId the dpId
327 * @param lportTag the lport tag
328 * @param priority the priority of the flow
329 * @param flowId the flowId
330 * @param conntrackState the conntrack state of the packets thats should be
332 * @param conntrackMask the conntrack mask
333 * @param addOrRemove whether to add or remove the flow
335 private void programConntrackForwardRule(BigInteger dpId, Integer priority, String flowId,
336 int conntrackState, int conntrackMask, short dispatcherTableId, short tableId, int addOrRemove) {
337 List<MatchInfoBase> matches = new ArrayList<>();
338 matches.add(new NxMatchInfo(NxMatchFieldType.ct_state, new long[] {conntrackState, conntrackMask}));
340 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(
341 new ArrayList<>(),dispatcherTableId);
343 flowId = "Fixed_Conntrk_Trk_" + dpId + "_" + flowId + dispatcherTableId;
344 syncFlow(dpId, tableId, flowId, priority, "ACL", 0, 0,
345 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
349 * Adds the rule to drop the unknown/invalid packets .
351 * @param dpId the dpId
352 * @param lportTag the lport tag
353 * @param priority the priority of the flow
354 * @param flowId the flowId
355 * @param conntrackState the conntrack state of the packets thats should be
357 * @param conntrackMask the conntrack mask
358 * @param addOrRemove whether to add or remove the flow
360 private void programConntrackDropRule(BigInteger dpId, Integer priority, String flowId,
361 int conntrackState, int conntrackMask, short tableId, int addOrRemove) {
362 List<MatchInfoBase> matches = new ArrayList<>();
363 matches.add(new NxMatchInfo(NxMatchFieldType.ct_state, new long[] {conntrackState, conntrackMask}));
365 List<InstructionInfo> instructions = new ArrayList<>();
366 List<ActionInfo> actionsInfos = new ArrayList<>();
367 actionsInfos.add(new ActionInfo(ActionType.drop_action, new String[] {}));
368 instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
369 flowId = "Fixed_Conntrk_NewDrop_" + dpId + "_" + flowId + tableId;
370 syncFlow(dpId, tableId, flowId, priority, "ACL", 0, 0,
371 AclConstants.COOKIE_ACL_BASE, matches, instructions, addOrRemove);
375 * Gets the dispatcher table resubmit instructions.
377 * @param actionsInfos the actions infos
378 * @return the instructions for dispatcher table resubmit
380 private List<InstructionInfo> getDispatcherTableResubmitInstructions(List<ActionInfo> actionsInfos,
381 short dispatcherTableId) {
382 List<InstructionInfo> instructions = new ArrayList<>();
383 actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] {Short.toString(dispatcherTableId)}));
384 instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
389 * Writes/remove the flow to/from the datastore.
390 * @param dpId the dpId
391 * @param tableId the tableId
392 * @param flowId the flowId
393 * @param priority the priority
394 * @param flowName the flow name
395 * @param idleTimeOut the idle timeout
396 * @param hardTimeOut the hard timeout
397 * @param cookie the cookie
398 * @param matches the list of matches to be written
399 * @param instructions the list of instruction to be written.
400 * @param addOrRemove add or remove the entries.
402 protected void syncFlow(BigInteger dpId, short tableId, String flowId, int priority, String flowName,
403 int idleTimeOut, int hardTimeOut, BigInteger cookie, List<? extends MatchInfoBase> matches,
404 List<InstructionInfo> instructions, int addOrRemove) {
405 if (addOrRemove == NwConstants.DEL_FLOW) {
406 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,flowId,
407 priority, flowName , idleTimeOut, hardTimeOut, cookie, matches, null);
408 LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
409 mdsalManager.removeFlow(flowEntity);
411 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId,
412 priority, flowName, idleTimeOut, hardTimeOut, cookie, matches, instructions);
413 LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
414 mdsalManager.installFlow(flowEntity);
419 * Gets the table miss flow id.
421 * @param tableId the table id
422 * @return the table miss flow id
424 private String getTableMissFlowId(short tableId) {
425 return String.valueOf(tableId);
432 * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
433 * getDataTreeChangeListener()
436 protected AclNodeListener getDataTreeChangeListener() {
437 return AclNodeListener.this;