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,
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);
158 addStatelessIngressAclTableMissFlow(dpnId);
159 addStatelessEgressAclTableMissFlow(dpnId);
164 * Adds the ingress acl table miss flow.
166 * @param dpId the dp id
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));
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);
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);
185 LOG.debug("Added Ingress ACL Table Miss Flows for dpn {}", dpId);
189 * Adds the ingress acl table miss flow.
191 * @param dpId the dp id
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 }));
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));
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);
208 synMatches = new ArrayList<>();
209 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
211 List<InstructionInfo> allowAllInstructions = new ArrayList<>();
212 allowAllInstructions.add(
213 new InstructionInfo(InstructionType.goto_table,
214 new long[] { NwConstants.INGRESS_ACL_NEXT_TABLE_ID }));
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);
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);
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);
235 LOG.debug("Added Stateless Ingress ACL Table Miss Flows for dpn {}", dpId);
239 * Adds the stateless egress acl table miss flow.
241 * @param dpId the dp id
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 }));
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 }));
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));
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);
262 synMatches = new ArrayList<>();
263 synMatches.add(new MatchInfo(MatchFieldType.tcp_flags, new long[] { AclConstants.TCP_FLAG_SYN_ACK }));
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);
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);
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);
283 LOG.debug("Added Stateless Egress ACL Table Miss Flows for dpn {}", dpId);
287 * Adds the egress acl table miss flow.
289 * @param dpId the dp id
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));
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);
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);
308 LOG.debug("Added Egress ACL Table Miss Flows for dpn {}", dpId);
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 );
325 * Adds the rule to forward the packets known packets.
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
333 * @param conntrackMask the conntrack mask
334 * @param addOrRemove whether to add or remove the flow
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}));
341 List<InstructionInfo> instructions = getDispatcherTableResubmitInstructions(
342 new ArrayList<>(),dispatcherTableId);
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);
350 * Adds the rule to drop the unknown/invalid packets .
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
358 * @param conntrackMask the conntrack mask
359 * @param addOrRemove whether to add or remove the flow
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}));
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);
376 * Gets the dispatcher table resubmit instructions.
378 * @param actionsInfos the actions infos
379 * @return the instructions for dispatcher table resubmit
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));
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.
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);
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);
420 * Gets the table miss flow id.
422 * @param tableId the table id
423 * @return the table miss flow id
425 private String getTableMissFlowId(short tableId) {
426 return String.valueOf(tableId);
433 * org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase#
434 * getDataTreeChangeListener()
437 protected AclNodeListener getDataTreeChangeListener() {
438 return AclNodeListener.this;