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.PostConstruct;
19 import javax.inject.Inject;
20 import javax.inject.Singleton;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.genius.datastoreutils.AsyncClusteredDataTreeChangeListenerBase;
24 import org.opendaylight.genius.infra.Datastore.Configuration;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
27 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
28 import org.opendaylight.genius.infra.TypedWriteTransaction;
29 import org.opendaylight.genius.mdsalutil.ActionInfo;
30 import org.opendaylight.genius.mdsalutil.FlowEntity;
31 import org.opendaylight.genius.mdsalutil.InstructionInfo;
32 import org.opendaylight.genius.mdsalutil.MDSALUtil;
33 import org.opendaylight.genius.mdsalutil.MatchInfo;
34 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
35 import org.opendaylight.genius.mdsalutil.NwConstants;
36 import org.opendaylight.genius.mdsalutil.NwConstants.NxmOfFieldType;
37 import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
38 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
39 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
41 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
42 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
43 import org.opendaylight.genius.mdsalutil.matches.MatchArpOp;
44 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
45 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
46 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.Uint64;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 public class VpnNodeListener extends AsyncClusteredDataTreeChangeListenerBase<Node, VpnNodeListener> {
58 private static final Logger LOG = LoggerFactory.getLogger(VpnNodeListener.class);
59 private static final String FLOWID_PREFIX = "L3.";
61 private final DataBroker broker;
62 private final ManagedNewTransactionRunner txRunner;
63 private final IMdsalApiManager mdsalManager;
64 private final JobCoordinator jobCoordinator;
65 private final List<Uint64> connectedDpnIds;
66 private final VpnConfig vpnConfig;
69 public VpnNodeListener(DataBroker dataBroker, IMdsalApiManager mdsalManager, JobCoordinator jobCoordinator,
70 VpnConfig vpnConfig) {
71 super(Node.class, VpnNodeListener.class);
72 this.broker = dataBroker;
73 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
74 this.mdsalManager = mdsalManager;
75 this.jobCoordinator = jobCoordinator;
76 this.connectedDpnIds = new CopyOnWriteArrayList<>();
77 this.vpnConfig = vpnConfig;
82 registerListener(LogicalDatastoreType.OPERATIONAL, broker);
86 protected InstanceIdentifier<Node> getWildCardPath() {
87 return InstanceIdentifier.create(Nodes.class).child(Node.class);
91 protected VpnNodeListener getDataTreeChangeListener() {
92 return VpnNodeListener.this;
96 protected void add(InstanceIdentifier<Node> identifier, Node add) {
97 Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(add.getId());
98 if (!connectedDpnIds.contains(dpId)) {
99 connectedDpnIds.add(dpId);
101 processNodeAdd(dpId);
105 protected void remove(InstanceIdentifier<Node> identifier, Node del) {
106 Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(del.getId());
107 connectedDpnIds.remove(dpId);
111 protected void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
114 public boolean isConnectedNode(Uint64 nodeId) {
115 return nodeId != null && connectedDpnIds.contains(nodeId);
118 private void processNodeAdd(Uint64 dpId) {
119 jobCoordinator.enqueueJob("VPNNODE-" + dpId.toString(),
120 () -> Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
121 LOG.debug("Received notification to install TableMiss entries for dpn {} ", dpId);
122 makeTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
123 makeL3IntfTblMissFlow(tx, dpId, NwConstants.ADD_FLOW);
124 makeSubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
125 makeIpv6SubnetRouteTableMissFlow(tx, dpId, NwConstants.ADD_FLOW);
126 createTableMissForVpnGwFlow(tx, dpId);
127 createL3GwMacArpFlows(tx, dpId);
128 programTableMissForVpnVniDemuxTable(tx, dpId, NwConstants.ADD_FLOW);
132 private void makeTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId, int addOrRemove)
133 throws ExecutionException, InterruptedException {
134 final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
135 // Instruction to goto L3 InterfaceTable
136 List<InstructionInfo> instructions =
137 Collections.singletonList(new InstructionGotoTable(NwConstants.L3_INTERFACE_TABLE));
138 List<MatchInfo> matches = new ArrayList<>();
139 FlowEntity flowEntityLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_LFIB_TABLE,
140 getTableMissFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, NwConstants.TABLE_MISS_FLOW),
141 NwConstants.TABLE_MISS_PRIORITY, "Table Miss", 0, 0, cookieTableMiss, matches, instructions);
143 if (addOrRemove == NwConstants.ADD_FLOW) {
144 LOG.debug("Invoking MDSAL to install Table Miss Entry");
145 mdsalManager.addFlow(confTx, flowEntityLfib);
147 mdsalManager.removeFlow(confTx, flowEntityLfib);
151 private void makeL3IntfTblMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
152 int addOrRemove) throws ExecutionException, InterruptedException {
153 List<InstructionInfo> instructions = new ArrayList<>();
154 List<MatchInfo> matches = new ArrayList<>();
155 final Uint64 cookieTableMiss = Uint64.valueOf("1030000", 16).intern();
156 // Instruction to goto L3 InterfaceTable
158 List<ActionInfo> actionsInfos = new ArrayList<>();
159 actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
160 instructions.add(new InstructionApplyActions(actionsInfos));
161 //instructions.add(new InstructionGotoTable(NwConstants.LPORT_DISPATCHER_TABLE));
163 FlowEntity flowEntityL3Intf = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_INTERFACE_TABLE,
164 getTableMissFlowRef(dpnId, NwConstants.L3_INTERFACE_TABLE, NwConstants.TABLE_MISS_FLOW),
165 NwConstants.TABLE_MISS_PRIORITY, "L3 Interface Table Miss", 0, 0, cookieTableMiss,
166 matches, instructions);
167 if (addOrRemove == NwConstants.ADD_FLOW) {
168 LOG.debug("Invoking MDSAL to install L3 interface Table Miss Entries");
169 mdsalManager.addFlow(confTx, flowEntityL3Intf);
171 mdsalManager.removeFlow(confTx, flowEntityL3Intf);
175 private void makeSubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
176 int addOrRemove) throws ExecutionException, InterruptedException {
177 List<ActionInfo> actionsInfos = new ArrayList<>();
178 List<InstructionInfo> instructions = new ArrayList<>();
179 actionsInfos.add(new ActionPuntToController());
180 int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
181 if (learnTimeout != 0) {
182 actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
183 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
186 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV4,
187 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
188 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
189 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
190 NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getType(),
191 NwConstants.NxmOfFieldType.NXM_OF_IP_DST.getFlowModHeaderLenInt()),
192 new ActionLearn.MatchFromField(NxmOfFieldType.OXM_OF_METADATA.getType(),
193 MetaDataUtil.METADATA_VPN_ID_OFFSET,
194 NxmOfFieldType.OXM_OF_METADATA.getType(), MetaDataUtil.METADATA_VPN_ID_OFFSET,
195 MetaDataUtil.METADATA_VPN_ID_BITLEN))));
198 instructions.add(new InstructionApplyActions(actionsInfos));
199 List<MatchInfo> matches = new ArrayList<>();
200 matches.add(MatchEthernetType.IPV4);
201 String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
202 NwConstants.ETHTYPE_IPV4, NwConstants.TABLE_MISS_FLOW);
203 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
204 NwConstants.TABLE_MISS_PRIORITY, "Subnet Route Table Miss", 0, 0,
205 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
207 if (addOrRemove == NwConstants.ADD_FLOW) {
208 mdsalManager.addFlow(confTx, flowEntity);
210 mdsalManager.removeFlow(confTx, flowEntity);
214 private void makeIpv6SubnetRouteTableMissFlow(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
215 int addOrRemove) throws ExecutionException, InterruptedException {
216 List<ActionInfo> actionsInfos = new ArrayList<>();
217 List<InstructionInfo> instructions = new ArrayList<>();
218 actionsInfos.add(new ActionPuntToController());
219 int learnTimeout = vpnConfig.getSubnetRoutePuntTimeout().intValue();
220 if (learnTimeout != 0) {
221 actionsInfos.add(new ActionLearn(0, learnTimeout, VpnConstants.DEFAULT_FLOW_PRIORITY,
222 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, 0, NwConstants.L3_SUBNET_ROUTE_TABLE,
225 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6,
226 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
227 NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
228 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
229 NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
230 NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getFlowModHeaderLenInt()),
231 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
232 MetaDataUtil.METADATA_VPN_ID_OFFSET,
233 NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
234 MetaDataUtil.METADATA_VPN_ID_OFFSET,
235 MetaDataUtil.METADATA_VPN_ID_BITLEN))));
238 instructions.add(new InstructionApplyActions(actionsInfos));
239 List<MatchInfo> matches = new ArrayList<>();
240 matches.add(MatchEthernetType.IPV6);
241 String flowRef = getSubnetRouteTableMissFlowRef(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE,
242 NwConstants.ETHTYPE_IPV6, NwConstants.TABLE_MISS_FLOW);
243 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_SUBNET_ROUTE_TABLE, flowRef,
244 NwConstants.TABLE_MISS_PRIORITY, "IPv6 Subnet Route Table Miss", 0, 0,
245 NwConstants.COOKIE_SUBNET_ROUTE_TABLE_MISS, matches, instructions);
246 if (addOrRemove == NwConstants.ADD_FLOW) {
247 LOG.debug("makeIpv6SubnetRouteTableMissFlow: Install Ipv6 Subnet Route Table Miss entry");
248 mdsalManager.addFlow(confTx, flowEntity);
250 LOG.debug("makeIpv6SubnetRouteTableMissFlow: Remove Ipv6 Subnet Route Table Miss entry");
251 mdsalManager.removeFlow(confTx, flowEntity);
255 private void programTableMissForVpnVniDemuxTable(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
256 int addOrRemove) throws ExecutionException, InterruptedException {
257 List<ActionInfo> actionsInfos = Collections.singletonList(new ActionNxResubmit(NwConstants
258 .LPORT_DISPATCHER_TABLE));
259 List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
260 List<MatchInfo> matches = new ArrayList<>();
261 String flowRef = getTableMissFlowRef(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
262 NwConstants.TABLE_MISS_FLOW);
263 FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3VNI_EXTERNAL_TUNNEL_DEMUX_TABLE,
264 flowRef, NwConstants.TABLE_MISS_PRIORITY, "VPN-VNI Demux Table Miss", 0, 0,
265 Uint64.valueOf("1080000", 16).intern(), matches, instructions);
267 if (addOrRemove == NwConstants.ADD_FLOW) {
268 mdsalManager.addFlow(confTx, flowEntity);
270 mdsalManager.removeFlow(confTx, flowEntity);
274 private void createTableMissForVpnGwFlow(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
275 List<MatchInfo> matches = new ArrayList<>();
276 List<ActionInfo> actionsInfos =
277 Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
278 List<InstructionInfo> instructions = Collections.singletonList(new InstructionApplyActions(actionsInfos));
279 FlowEntity flowEntityMissforGw = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_GW_MAC_TABLE,
280 getTableMissFlowRef(dpId, NwConstants.L3_GW_MAC_TABLE, NwConstants.TABLE_MISS_FLOW),
281 NwConstants.TABLE_MISS_PRIORITY, "L3 Gw Mac Table Miss", 0, 0,
282 Uint64.valueOf("1080000", 16).intern(), matches,
284 LOG.trace("Invoking MDSAL to install L3 Gw Mac Table Miss Entry");
285 mdsalManager.addFlow(confTx, flowEntityMissforGw);
288 private void createL3GwMacArpFlows(TypedWriteTransaction<Configuration> confTx, Uint64 dpId) {
289 FlowEntity arpReqGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
290 NwConstants.ARP_REQUEST, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REQUEST),
291 () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
292 LOG.trace("Invoking MDSAL to install Arp Rquest Match Flow for table {}", NwConstants.L3_GW_MAC_TABLE);
293 mdsalManager.addFlow(confTx, arpReqGwMacTbl);
294 FlowEntity arpRepGwMacTbl = ArpResponderUtil.createArpDefaultFlow(dpId, NwConstants.L3_GW_MAC_TABLE,
295 NwConstants.ARP_REPLY, () -> Arrays.asList(MatchEthernetType.ARP, MatchArpOp.REPLY),
296 () -> Collections.singletonList(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE)));
297 LOG.trace("Invoking MDSAL to install Arp Reply Match Flow for Table {} ", NwConstants.L3_GW_MAC_TABLE);
298 mdsalManager.addFlow(confTx, arpRepGwMacTbl);
301 private String getTableMissFlowRef(Uint64 dpnId, short tableId, int tableMiss) {
302 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + tableMiss
306 private String getSubnetRouteTableMissFlowRef(Uint64 dpnId, short tableId, int etherType, int tableMiss) {
307 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + etherType
308 + NwConstants.FLOWID_SEPARATOR + tableMiss + FLOWID_PREFIX;