440dac2875241a5422cbf6b8ffbf5b9ebe8a6f32
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnNodeListener.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 package org.opendaylight.netvirt.vpnmanager;
9
10 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
11
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;
54
55 @Singleton
56 public class VpnNodeListener extends AsyncClusteredDataTreeChangeListenerBase<Node, VpnNodeListener> {
57
58     private static final Logger LOG = LoggerFactory.getLogger(VpnNodeListener.class);
59     private static final String FLOWID_PREFIX = "L3.";
60
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;
67
68     @Inject
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;
78     }
79
80     @PostConstruct
81     public void start() {
82         registerListener(LogicalDatastoreType.OPERATIONAL, broker);
83     }
84
85     @Override
86     protected InstanceIdentifier<Node> getWildCardPath() {
87         return InstanceIdentifier.create(Nodes.class).child(Node.class);
88     }
89
90     @Override
91     protected VpnNodeListener getDataTreeChangeListener() {
92         return VpnNodeListener.this;
93     }
94
95     @Override
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);
100         }
101         processNodeAdd(dpId);
102     }
103
104     @Override
105     protected void remove(InstanceIdentifier<Node> identifier, Node del) {
106         Uint64 dpId = MDSALUtil.getDpnIdFromNodeName(del.getId());
107         connectedDpnIds.remove(dpId);
108     }
109
110     @Override
111     protected void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
112     }
113
114     public boolean isConnectedNode(Uint64 nodeId) {
115         return nodeId != null && connectedDpnIds.contains(nodeId);
116     }
117
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);
129             })), 3);
130     }
131
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);
142
143         if (addOrRemove == NwConstants.ADD_FLOW) {
144             LOG.debug("Invoking MDSAL to install Table Miss Entry");
145             mdsalManager.addFlow(confTx, flowEntityLfib);
146         } else {
147             mdsalManager.removeFlow(confTx, flowEntityLfib);
148         }
149     }
150
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
157
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));
162
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);
170         } else {
171             mdsalManager.removeFlow(confTx, flowEntityL3Intf);
172         }
173     }
174
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,
184                     0, 0,
185                     Arrays.asList(
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))));
196         }
197
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);
206
207         if (addOrRemove == NwConstants.ADD_FLOW) {
208             mdsalManager.addFlow(confTx, flowEntity);
209         } else {
210             mdsalManager.removeFlow(confTx, flowEntity);
211         }
212     }
213
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,
223                     0, 0,
224                     Arrays.asList(
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))));
236         }
237
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);
249         } else {
250             LOG.debug("makeIpv6SubnetRouteTableMissFlow: Remove Ipv6 Subnet Route Table Miss entry");
251             mdsalManager.removeFlow(confTx, flowEntity);
252         }
253     }
254
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);
266
267         if (addOrRemove == NwConstants.ADD_FLOW) {
268             mdsalManager.addFlow(confTx, flowEntity);
269         } else {
270             mdsalManager.removeFlow(confTx, flowEntity);
271         }
272     }
273
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,
283             instructions);
284         LOG.trace("Invoking MDSAL to install L3 Gw Mac Table Miss Entry");
285         mdsalManager.addFlow(confTx, flowEntityMissforGw);
286     }
287
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);
299     }
300
301     private String getTableMissFlowRef(Uint64 dpnId, short tableId, int tableMiss) {
302         return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + tableMiss
303                 + FLOWID_PREFIX;
304     }
305
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;
309     }
310 }