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
8 package org.opendaylight.netvirt.vpnmanager;
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.concurrent.CopyOnWriteArrayList;
17 import java.util.concurrent.ExecutionException;
18 import javax.annotation.PreDestroy;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.genius.infra.Datastore.Configuration;
22 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
23 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
24 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
25 import org.opendaylight.genius.infra.TypedWriteTransaction;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.FlowEntity;
28 import org.opendaylight.genius.mdsalutil.InstructionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.NwConstants.NxmOfFieldType;
34 import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
35 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
36 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
37 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
38 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
39 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
40 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
41 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
42 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
43 import org.opendaylight.infrautils.utils.concurrent.Executors;
44 import org.opendaylight.mdsal.binding.api.DataBroker;
45 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
46 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
47 import org.opendaylight.serviceutils.tools.listener.AbstractAsyncDataTreeChangeListener;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 public class VpnNodeListener extends AbstractAsyncDataTreeChangeListener<Node> {
59 private static final Logger LOG = LoggerFactory.getLogger(VpnNodeListener.class);
60 private static final String FLOWID_PREFIX = "L3.";
62 private final DataBroker broker;
63 private final ManagedNewTransactionRunner txRunner;
64 private final IMdsalApiManager mdsalManager;
65 private final JobCoordinator jobCoordinator;
66 private final List<Uint64> connectedDpnIds;
67 private final VpnConfig vpnConfig;
70 public VpnNodeListener(DataBroker dataBroker, IMdsalApiManager mdsalManager, JobCoordinator jobCoordinator,
71 VpnConfig vpnConfig) {
72 super(dataBroker, LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class).child(Node.class),
73 Executors.newListeningSingleThreadExecutor("VpnNodeListener", LOG));
74 this.broker = dataBroker;
75 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
76 this.mdsalManager = mdsalManager;
77 this.jobCoordinator = jobCoordinator;
78 this.connectedDpnIds = new CopyOnWriteArrayList<>();
79 this.vpnConfig = vpnConfig;
84 LOG.info("{} start", getClass().getSimpleName());
91 Executors.shutdownAndAwaitTermination(getExecutorService());
95 public void add(InstanceIdentifier<Node> identifier, Node add) {
96 Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(add.getId());
97 if (!connectedDpnIds.contains(dpId)) {
98 connectedDpnIds.add(dpId);
100 processNodeAdd(dpId);
104 public void remove(InstanceIdentifier<Node> identifier, Node del) {
105 Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(del.getId());
106 connectedDpnIds.remove(dpId);
110 public void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
113 public boolean isConnectedNode(Uint64 nodeId) {
114 return nodeId != null && connectedDpnIds.contains(nodeId);
117 private void processNodeAdd(Uint64 dpId) {
118 jobCoordinator.enqueueJob("VPNNODE-" + dpId.toString(),
119 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
120 LOG.debug("Received notification to install TableMiss entries for dpn {} ", dpId);
121 makeTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
122 makeL3IntfTblMissFlow(tx, dpId, NwConstants.ADD_FLOW);
123 makeSubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
124 makeIpv6SubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
125 createTableMissForVpnGwFlow(tx, dpId);
126 createL3GwMacArpFlows(tx, dpId);
127 programTableMissForVpnVniDemuxTable(tx, dpId, NwConstants.ADD_FLOW);
131 private void makeTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId, int addOrRemove)
132 throws ExecutionException, InterruptedException {
133 final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
134 // Instruction to goto L3 InterfaceTable
135 List<InstructionInfo> instructions =
136 Collections.singletonList(new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
137 List<MatchInfo> matches = new ArrayList<>();
138 FlowEntity flowEntityLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_LFIB_TABLE,
139 getTableMissFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, NwConstants.TABLE_MISS_FLOW),
140 NwConstants.TABLE_MISS_PRIORITY, "Table Miss", 0, 0, cookieTableMiss, matches, instructions);
142 if (addOrRemove == NwConstants.ADD_FLOW) {
143 LOG.debug("Invoking MDSAL to install Table Miss Entry");
144 mdsalManager.addFlow(confTx, flowEntityLfib);
146 mdsalManager.removeFlow(confTx, flowEntityLfib);
150 private void makeL3IntfTblMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
151 int addOrRemove) throws ExecutionException, InterruptedException {
152 List<InstructionInfo> instructions = new ArrayList<>();
153 List<MatchInfo> matches = new ArrayList<>();
154 final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
155 // Instruction to goto L3 InterfaceTable
157 List<ActionInfo> actionsInfos = new ArrayList<>();
158 actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
159 instructions.add(new InstructionApplyActions(actionsInfos));
160 //instructions.add(new InstructionGotoTable(NwConstants.LPORT_DISPATCHER_TABLE));
162 FlowEntity flowEntityL3Intf = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_INTERFACE_TABLE,
163 getTableMissFlowRef(dpnId, NwConstants.L3_INTERFACE_TABLE, NwConstants.TABLE_MISS_FLOW),
164 NwConstants.TABLE_MISS_PRIORITY, "L3 Interface Table Miss", 0, 0, cookieTableMiss,
165 matches, instructions);
166 if (addOrRemove == NwConstants.ADD_FLOW) {
167 LOG.debug("Invoking MDSAL to install L3 interface Table Miss Entries");
168 mdsalManager.addFlow(confTx, flowEntityL3Intf);
170 mdsalManager.removeFlow(confTx, flowEntityL3Intf);
174 private void makeSubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
175 int addOrRemove) throws ExecutionException, InterruptedException {
176 List<ActionInfo> actionsInfos = new ArrayList<>();
177 List<InstructionInfo> instructions = new ArrayList<>();
178 actionsInfos.add(new ActionPuntToController());
179 int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
180 if (learnTimeout != 0) {
181 actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
182 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
185 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV4,
186 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
187 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
188 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
189 NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
190 NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getFlowModHeaderLenInt()),
191 new ActionLearn.MatchFromField(NxmOfFieldType.OXM_OF_METADATA.getType(),
192 MetaDataUtil.METADATA_VPN_ID_OFFSET,
193 NxmOfFieldType.OXM_OF_METADATA.getType(), MetaDataUtil.METADATA_VPN_ID_OFFSET,
194 MetaDataUtil.METADATA_VPN_ID_BITLEN))));
197 instructions.add(new InstructionApplyActions(actionsInfos));
198 List<MatchInfo> matches = new ArrayList<>();
199 matches.add(MatchEthernetType.IPV4);
200 String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
201 NwConstants.ETHTYPE_IPV4, NwConstants.TABLE_MISS_FLOW);
202 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
203 NwConstants.TABLE_MISS_PRIORITY, "Subnet Route Table Miss", 0, 0,
204 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
206 if (addOrRemove == NwConstants.ADD_FLOW) {
207 mdsalManager.addFlow(confTx, flowEntity);
209 mdsalManager.removeFlow(confTx, flowEntity);
213 private void makeIpv6SubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
214 int addOrRemove) throws ExecutionException, InterruptedException {
215 List<ActionInfo> actionsInfos = new ArrayList<>();
216 List<InstructionInfo> instructions = new ArrayList<>();
217 actionsInfos.add(new ActionPuntToController());
218 int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
219 if (learnTimeout != 0) {
220 actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
221 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
224 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6,
225 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
226 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
227 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
228 NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
229 NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getFlowModHeaderLenInt()),
230 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
231 MetaDataUtil.METADATA_VPN_ID_OFFSET,
232 NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
233 MetaDataUtil.METADATA_VPN_ID_OFFSET,
234 MetaDataUtil.METADATA_VPN_ID_BITLEN))));
237 instructions.add(new InstructionApplyActions(actionsInfos));
238 List<MatchInfo> matches = new ArrayList<>();
239 matches.add(MatchEthernetType.IPV6);
240 String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
241 NwConstants.ETHTYPE_IPV6, NwConstants.TABLE_MISS_FLOW);
242 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
243 NwConstants.TABLE_MISS_PRIORITY, "IPv6 Subnet Route Table Miss", 0, 0,
244 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
245 if (addOrRemove == NwConstants.ADD_FLOW) {
246 LOG.debug("makeIpv6SubnetRouteTableMissFlow: Install Ipv6 Subnet Route Table Miss entry");
247 mdsalManager.addFlow(confTx, flowEntity);
249 LOG.debug("makeIpv6SubnetRouteTableMissFlow: Remove Ipv6 Subnet Route Table Miss entry");
250 mdsalManager.removeFlow(confTx, flowEntity);
254 private void programTableMissForVpnVniDemuxTable(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
255 int addOrRemove) throws ExecutionException, InterruptedException {
256 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionNxResubmit(NwConstants
257 .LPORT_DISPATCHER_TABLE));
258 List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
259 List<MatchInfo> matches = new ArrayList<>();
260 String flowRef = getTableMissFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
261 NwConstants.TABLE_MISS_FLOW);
262 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
263 flowRef, NwConstants.TABLE_MISS_PRIORITY, "VPN-VNI Demux Table Miss", 0, 0,
264 Uint64.valueOf("1080000", 16).intern(), matches, instructions);
266 if (addOrRemove == NwConstants.ADD_FLOW) {
267 mdsalManager.addFlow(confTx, flowEntity);
269 mdsalManager.removeFlow(confTx, flowEntity);
273 private void createTableMissForVpnGwFlow(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
274 List<MatchInfo> matches = new ArrayList<>();
275 List<ActionInfo> actionsInfos =
276 Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
277 List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
278 FlowEntity flowEntityMissforGw = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_GW_MAC_TABLE,
279 getTableMissFlowRef(dpId, NwConstants.L3_GW_MAC_TABLE, NwConstants.TABLE_MISS_FLOW),
280 NwConstants.TABLE_MISS_PRIORITY, "L3 Gw Mac Table Miss", 0, 0,
281 Uint64.valueOf("1080000", 16).intern(), matches,
283 LOG.trace("Invoking MDSAL to install L3 Gw Mac Table Miss Entry");
284 mdsalManager.addFlow(confTx, flowEntityMissforGw);
287 private void createL3GwMacArpFlows(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
288 FlowEntity arpReqGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
289 NwConstants.ARP_REQUEST, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST),
290 () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
291 LOG.trace("Invoking MDSAL to install Arp Rquest Match Flow for table {}", NwConstants.L3_GW_MAC_TABLE);
292 mdsalManager.addFlow(confTx, arpReqGwMacTbl);
293 FlowEntity arpRepGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
294 NwConstants.ARP_REPLY, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REPLY),
295 () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
296 LOG.trace("Invoking MDSAL to install Arp Reply Match Flow for Table {} ", NwConstants.L3_GW_MAC_TABLE);
297 mdsalManager.addFlow(confTx, arpRepGwMacTbl);
300 private String getTableMissFlowRef(Uint64 dpnId, short tableId, int tableMiss) {
301 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + tableMiss
305 private String getSubnetRouteTableMissFlowRef(Uint64 dpnId, short tableId, int etherType, int tableMiss) {
306 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + etherType
307 + NwConstants.FLOWID_SEPARATOR + tableMiss + FLOWID_PREFIX;