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.natservice.internal;
10 import com.google.common.base.Optional;
11 import com.google.common.collect.Sets;
13 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
14 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
15 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
16 import org.opendaylight.genius.mdsalutil.*;
17 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCase;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.*;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.RoutersKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMapping;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.IpMappingKey;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.map.ip.mapping.IpMap;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMapping;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.IpPortMappingKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.IntextIpProtocolType;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.IpPortMap;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.intext.ip.port.map.ip.port.mapping.intext.ip.protocol.type.ip.port.map.IpPortExternal;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitch;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.napt.switches.RouterToNaptSwitchKey;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.common.RpcResult;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
62 import java.math.BigInteger;
63 import java.util.ArrayList;
64 import java.util.List;
66 import java.util.concurrent.ExecutionException;
67 import java.util.concurrent.Future;
68 import java.util.HashMap;
70 public class NaptSwitchHA {
71 private static final Logger LOG = LoggerFactory.getLogger(NaptSwitchHA.class);
72 private final DataBroker dataBroker;
73 private IMdsalApiManager mdsalManager;
74 private ItmRpcService itmManager;
75 private OdlInterfaceRpcService interfaceManager;
76 private IdManagerService idManager;
77 private NAPTSwitchSelector naptSwitchSelector;
78 private ExternalRoutersListener externalRouterListener;
79 private IBgpManager bgpManager;
80 private VpnRpcService vpnService;
81 private FibRpcService fibService;
83 private List<String> externalIpsCache;
84 private HashMap<String,Long> externalIpsLabel;
85 public NaptSwitchHA(DataBroker broker,NAPTSwitchSelector selector){
87 naptSwitchSelector = selector;
90 public void setItmManager(ItmRpcService itmManager) {
91 this.itmManager = itmManager;
94 public void setMdsalManager(IMdsalApiManager mdsalManager) {
95 this.mdsalManager = mdsalManager;
98 public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
99 this.interfaceManager = interfaceManager;
102 public void setIdManager(IdManagerService idManager) {
103 this.idManager = idManager;
106 void setExternalRoutersListener(ExternalRoutersListener externalRoutersListener) {
107 this.externalRouterListener = externalRoutersListener;
110 public void setBgpManager(IBgpManager bgpManager) {
111 this.bgpManager = bgpManager;
114 public void setVpnService(VpnRpcService vpnService) {
115 this.vpnService = vpnService;
118 public void setFibService(FibRpcService fibService) {
119 this.fibService = fibService;
122 /* This method checks the switch that gone down is a NaptSwitch for a router.
123 If it is a NaptSwitch
124 1) selects new NAPT switch
125 2) installs nat flows in new NAPT switch
126 table 21(FIB)->26(PSNAT)->group(resubmit/napttunnel)->36(Terminating)->46(outbound)->47(resubmit)->21
127 3) modify the group and miss entry flow in other vSwitches pointing to newNaptSwitch
128 4) Remove nat flows in oldNaptSwitch
130 /*public void handleNaptSwitchDown(BigInteger dpnId){
132 LOG.debug("handleNaptSwitchDown method is called with dpnId {}",dpnId);
133 BigInteger naptSwitch;
135 NaptSwitches naptSwitches = NatUtil.getNaptSwitch(dataBroker);
136 if (naptSwitches == null || naptSwitches.getRouterToNaptSwitch() == null || naptSwitches.getRouterToNaptSwitch().isEmpty()) {
137 LOG.debug("NaptSwitchDown: NaptSwitch is not allocated for none of the routers");
140 for (RouterToNaptSwitch routerToNaptSwitch : naptSwitches.getRouterToNaptSwitch()) {
141 String routerName = routerToNaptSwitch.getRouterName();
142 naptSwitch = routerToNaptSwitch.getPrimarySwitchId();
143 boolean naptStatus = isNaptSwitchDown(routerName,dpnId,naptSwitch);
145 LOG.debug("NaptSwitchDown: Switch with DpnId {} is not naptSwitch for router {}",
148 removeSnatFlowsInOldNaptSwitch(routerName,naptSwitch);
152 } catch (Exception ex) {
153 LOG.error("Exception in handleNaptSwitchDown method {}",ex);
157 protected void removeSnatFlowsInOldNaptSwitch(String routerName, BigInteger naptSwitch,HashMap<String,Long> externalIpmap) {
158 externalIpsLabel = externalIpmap;
159 //remove SNAT flows in old NAPT SWITCH
160 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
161 if (routerId == NatConstants.INVALID_ID) {
162 LOG.error("Invalid routerId returned for routerName {}",routerName);
166 //Remove the Terminating Service table entry which forwards the packet to Outbound NAPT Table
167 String tsFlowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, routerId);
168 FlowEntity tsNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.TERMINATING_SERVICE_TABLE, tsFlowRef);
170 LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and router ID {}"
171 ,NatConstants.TERMINATING_SERVICE_TABLE, naptSwitch, routerId);
172 mdsalManager.removeFlow(tsNatFlowEntity);
174 //Remove the Outbound flow entry which forwards the packet to Outbound NAPT Table
175 String outboundNatFlowRef = externalRouterListener.getFlowRefOutbound(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, routerId);
176 FlowEntity outboundNatFlowEntity = NatUtil.buildFlowEntity(naptSwitch,
177 NatConstants.OUTBOUND_NAPT_TABLE, outboundNatFlowRef);
178 LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and router ID {}"
179 ,NatConstants.OUTBOUND_NAPT_TABLE, naptSwitch, routerId);
180 mdsalManager.removeFlow(outboundNatFlowEntity);
182 //Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for inbound traffic matching on the router ID.
183 String naptPFibflowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.NAPT_PFIB_TABLE, routerId);
184 FlowEntity naptPFibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.NAPT_PFIB_TABLE,naptPFibflowRef);
185 LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and router ID {}",
186 NatConstants.NAPT_PFIB_TABLE, naptSwitch, routerId);
187 mdsalManager.removeFlow(naptPFibFlowEntity);
189 //Remove the NAPT_PFIB_TABLE(47) flow entry forwards the packet to Fib Table for outbound traffic matching on the vpn ID.
190 boolean switchSharedByRouters = false;
191 Uuid extNetworkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
192 if (extNetworkId != null) {
193 List<String> routerNamesAssociated = getRouterIdsForExtNetwork(extNetworkId);
194 if (routerNamesAssociated != null) {
195 for (String routerNameAssociated : routerNamesAssociated) {
196 if (!routerNameAssociated.equals(routerName)) {
197 Long routerIdAssociated = NatUtil.getVpnId(dataBroker,routerNameAssociated);
198 BigInteger naptDpn = NatUtil.getPrimaryNaptfromRouterId(dataBroker,routerIdAssociated);
199 if (naptDpn != null && naptDpn.equals(naptSwitch)) {
200 LOG.debug("Napt switch {} is also acting as primary for router {}",routerIdAssociated);
201 switchSharedByRouters = true;
206 if (!switchSharedByRouters) {
207 Long vpnId = getVpnIdForRouter(routerId);
208 if (vpnId != NatConstants.INVALID_ID) {
209 String naptFibflowRef = externalRouterListener.getFlowRefTs(naptSwitch, NatConstants.NAPT_PFIB_TABLE, vpnId);
210 FlowEntity naptFibFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.NAPT_PFIB_TABLE,naptFibflowRef);
211 LOG.info("Remove the flow in table {} for the old napt switch with the DPN ID {} and vpnId {}",
212 NatConstants.NAPT_PFIB_TABLE, naptSwitch, vpnId);
213 mdsalManager.removeFlow(naptFibFlowEntity);
215 LOG.error("Invalid vpnId retrieved for routerId {}",routerId);
222 //Remove Fib entries,tables 20->44 ,36-> 44
223 String vpnName = getExtNetworkVpnName(routerId);
224 if (vpnName == null) {
225 LOG.debug("Vpn is not associated to externalN/w of router {}",routerName);
227 if (externalIpsLabel != null) {
228 for (String externalIp : externalIpsLabel.keySet()) {
229 Long label = externalIpsLabel.get(externalIp);
230 externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerId, externalIp, vpnName,label);
231 LOG.debug("Successfully removed fib entries in old naptswitch {} for router {} and externalIps {} label {}",
232 naptSwitch, routerId,externalIp,label);
235 List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
236 if (externalIps != null) {
237 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
238 if (networkId != null) {
239 externalRouterListener.clearFibTsAndReverseTraffic(naptSwitch, routerId, networkId, externalIps, null);
240 LOG.debug("Successfully removed fib entries in old naptswitch {} for router {} with networkId {} and externalIps {}",
241 naptSwitch,routerId,networkId,externalIps);
243 LOG.debug("External network not associated to router {}", routerId);
246 LOG.debug("ExternalIps not found for router {}",routerName);
251 //For the router ID get the internal IP , internal port and the corresponding external IP and external Port.
252 IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker, routerId);
253 if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
254 LOG.debug("No Internal Ip Port mapping associated to router {}, no flows need to be removed in" +
255 "oldNaptSwitch {}", routerId, naptSwitch);
258 BigInteger cookieSnatFlow = NatUtil.getCookieNaptFlow(routerId);
259 List<IntextIpProtocolType> intextIpProtocolTypes = ipPortMapping.getIntextIpProtocolType();
260 for(IntextIpProtocolType intextIpProtocolType : intextIpProtocolTypes) {
261 if (intextIpProtocolType.getIpPortMap() == null || intextIpProtocolType.getIpPortMap().isEmpty()) {
262 LOG.debug("No {} session associated to router {},no flows need to be removed in oldNaptSwitch {}",
263 intextIpProtocolType.getProtocol(),routerId,naptSwitch);
266 List<IpPortMap> ipPortMaps = intextIpProtocolType.getIpPortMap();
267 for(IpPortMap ipPortMap : ipPortMaps) {
268 String ipPortInternal = ipPortMap.getIpPortInternal();
269 String[] ipPortParts = ipPortInternal.split(":");
270 if(ipPortParts.length != 2) {
271 LOG.error("Unable to retrieve the Internal IP and port");
274 String internalIp = ipPortParts[0];
275 String internalPort = ipPortParts[1];
277 //Build and remove flow in outbound NAPT table
278 String switchFlowRef = NatUtil.getNaptFlowRef(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE, String.valueOf(routerId),
279 internalIp, Integer.valueOf(internalPort));
280 FlowEntity outboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.OUTBOUND_NAPT_TABLE,
281 cookieSnatFlow, switchFlowRef);
283 LOG.info("Remove the flow in table {} for old napt switch with the DPN ID {} and router ID {}",
284 NatConstants.OUTBOUND_NAPT_TABLE,naptSwitch, routerId);
285 mdsalManager.removeFlow(outboundNaptFlowEntity);
287 IpPortExternal ipPortExternal = ipPortMap.getIpPortExternal();
288 if (ipPortExternal == null) {
289 LOG.debug("External Ipport mapping not found for internalIp {} with port {} for router", internalIp,
290 internalPort, routerId);
293 String externalIp = ipPortExternal.getIpAddress();
294 int externalPort = ipPortExternal.getPortNum();
296 //Build and remove flow in inbound NAPT table
297 switchFlowRef = NatUtil.getNaptFlowRef(naptSwitch, NatConstants.INBOUND_NAPT_TABLE, String.valueOf(routerId),
298 externalIp, externalPort);
299 FlowEntity inboundNaptFlowEntity = NatUtil.buildFlowEntity(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
300 cookieSnatFlow, switchFlowRef);
302 LOG.info("Remove the flow in table {} for old napt switch with the DPN ID {} and router ID {}",
303 NatConstants.INBOUND_NAPT_TABLE,naptSwitch, routerId);
304 mdsalManager.removeFlow(inboundNaptFlowEntity);
310 private List<String> getRouterIdsForExtNetwork(Uuid extNetworkId) {
311 List<String> routerUuidsAsString = new ArrayList<>();
312 InstanceIdentifier<Networks> extNetwork = InstanceIdentifier.builder(ExternalNetworks.class).child
313 (Networks.class, new NetworksKey(extNetworkId)).build();
314 Optional<Networks> extNetworkData = NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, extNetwork);
315 if (extNetworkData.isPresent()) {
316 List<Uuid> routerUuids= extNetworkData.get().getRouterIds();
317 if (routerUuids != null){
318 for(Uuid routerUuid : routerUuids){
319 routerUuidsAsString.add(routerUuid.getValue());
323 return routerUuidsAsString;
326 public boolean isNaptSwitchDown(String routerName, BigInteger dpnId , BigInteger naptSwitch,Long routerVpnId,List<String> externalIpCache) {
327 externalIpsCache = externalIpCache;
328 if (!naptSwitch.equals(dpnId)) {
329 LOG.debug("DpnId {} is not a naptSwitch {} for Router {}",dpnId, naptSwitch, routerName);
332 LOG.debug("NaptSwitch {} is down for Router {}", naptSwitch, routerName);
333 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
334 if (routerId == NatConstants.INVALID_ID) {
335 LOG.error("Invalid routerId returned for routerName {}", routerName);
338 //elect a new NaptSwitch
339 naptSwitch = naptSwitchSelector.selectNewNAPTSwitch(routerName);
340 if (naptSwitch.equals(BigInteger.ZERO)) {
341 LOG.info("No napt switch is elected since all the switches for router {} are down",routerName);
342 boolean naptUpdatedStatus = updateNaptSwitch(routerName,naptSwitch);
343 if(!naptUpdatedStatus) {
344 LOG.debug("Failed to update naptSwitch {} for router {} in ds", naptSwitch,routerName);
347 if (externalIpsCache != null) {
348 String vpnName = getExtNetworkVpnName(routerId);
349 if (vpnName != null) {
350 //List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker, routerId);
351 //if (externalIps != null) {
352 for (String externalIp : externalIpsCache) {
353 externalRouterListener.clearBgpRoutes(externalIp, vpnName);
356 LOG.debug("vpn is not associated to extn/w for router {}", routerName);
359 LOG.debug("No ExternalIps found for subnets under router {}, no bgp routes need to be cleared",routerName);
363 //checking elected switch health status
364 if (!getSwitchStatus(naptSwitch)) {
365 LOG.error("Newly elected Napt switch {} for router {} is down", naptSwitch, routerName);
368 LOG.debug("New NaptSwitch {} is up for Router {} and can proceed for flow installation",naptSwitch, routerName);
369 //update napt model for new napt switch
370 boolean naptUpdated = updateNaptSwitch(routerName, naptSwitch);
372 //update group of ordinary switch point to naptSwitch tunnel port
373 updateNaptSwitchBucketStatus(routerName, naptSwitch);
375 LOG.error("Failed to update naptSwitch model for newNaptSwitch {} for router {}",naptSwitch, routerName);
378 //update table26 forward packets to table46(outbound napt table)
379 FlowEntity flowEntity = buildSnatFlowEntityForNaptSwitch(naptSwitch, routerName, routerVpnId, NatConstants.ADD_FLOW);
380 if (flowEntity == null) {
381 LOG.debug("Failed to populate flowentity for router {} in naptSwitch {}", routerName, naptSwitch);
383 LOG.debug("Successfully installed flow in naptSwitch {} for router {}", naptSwitch, routerName);
384 mdsalManager.installFlow(flowEntity);
387 installSnatFlows(routerName,routerId,naptSwitch,routerVpnId);
389 boolean flowInstalledStatus = handleNatFlowsInNewNaptSwitch(routerId, dpnId, naptSwitch,routerVpnId);
390 if (flowInstalledStatus) {
391 LOG.debug("Installed all active session flows in newNaptSwitch {} for routerName {}", naptSwitch, routerName);
393 LOG.error("Failed to install flows in newNaptSwitch {} for routerId {}", naptSwitch, routerId);
396 //remove group in new naptswitch, coz this switch acted previously as ordinary switch
397 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
398 GroupEntity groupEntity = null;
400 groupEntity = MDSALUtil.buildGroupEntity(naptSwitch, groupId, routerName,
401 GroupTypes.GroupAll, null);
402 LOG.info("NAT Service : Removing NAPT Group in new naptSwitch {}", naptSwitch);
403 mdsalManager.removeGroup(groupEntity);
404 } catch (Exception ex) {
405 LOG.debug("NAT Service : Failed to remove group in new naptSwitch {} : {}",groupEntity,ex);
410 private String getExtNetworkVpnName(long routerId) {
411 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
412 if(networkId == null) {
413 LOG.error("networkId is null for the router ID {}", routerId);
415 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
416 if (vpnName != null) {
417 LOG.debug("retrieved vpn name {} associated with ext nw {} in router {}",
418 vpnName,networkId,routerId);
421 LOG.error("No VPN associated with ext nw {} belonging to routerId {}",
422 networkId, routerId);
428 public void updateNaptSwitchBucketStatus(String routerName, BigInteger naptSwitch) {
429 LOG.debug("updateNaptSwitchBucketStatus method is called");
431 List<BigInteger> dpnList = naptSwitchSelector.getDpnsForVpn(routerName);
432 //List<BigInteger> dpnList = getDpnListForRouter(routerName);
433 if (dpnList == null || dpnList.isEmpty()) {
434 LOG.debug("No switches found for router {}",routerName);
437 for (BigInteger dpn : dpnList) {
438 if (!dpn.equals(naptSwitch)) {
439 LOG.debug("Updating SNAT_TABLE missentry for DpnId {} which is not naptSwitch for router {}",dpn,routerName);
440 List<BucketInfo> bucketInfoList = handleGroupInNeighborSwitches(dpn, routerName, naptSwitch);
441 if (bucketInfoList == null) {
442 LOG.debug("Failed to populate bucketInfo for orinaryswitch {} whose naptSwitch {} for router {} ",
443 dpn,naptSwitch,routerName);
446 modifySnatGroupEntry(dpn, bucketInfoList, routerName);
451 private boolean handleNatFlowsInNewNaptSwitch(Long routerId,BigInteger oldNaptSwitch, BigInteger newNaptSwitch,Long routerVpnId) {
452 LOG.debug("Proceeding to install flows in newNaptSwitch {} for routerId {}", newNaptSwitch,routerId);
453 IpPortMapping ipPortMapping = NatUtil.getIportMapping(dataBroker,routerId);
454 if (ipPortMapping == null || ipPortMapping.getIntextIpProtocolType() == null || ipPortMapping.getIntextIpProtocolType().isEmpty()) {
455 LOG.debug("No Internal Ip Port mapping associated to router {}, no flows need to be installed in" +
456 "newNaptSwitch {}", routerId, newNaptSwitch);
460 Long vpnId = getVpnIdForRouter(routerId);
461 if (vpnId == NatConstants.INVALID_ID) {
462 LOG.error("Invalid vpnId for routerId {}",routerId);
466 if(routerId.equals(routerVpnId)) {
467 bgpVpnId = NatConstants.INVALID_ID;
469 bgpVpnId = routerVpnId;
471 LOG.debug("retrieved bgpVpnId {} for router {}",bgpVpnId,routerId);
472 for (IntextIpProtocolType protocolType : ipPortMapping.getIntextIpProtocolType()) {
473 if (protocolType.getIpPortMap() == null || protocolType.getIpPortMap().isEmpty()) {
474 LOG.debug("No {} session associated to router {}", protocolType.getProtocol(), routerId);
477 for (IpPortMap intIpPortMap : protocolType.getIpPortMap()) {
478 String internalIpAddress = intIpPortMap.getIpPortInternal().split(":")[0];
479 String intportnum = intIpPortMap.getIpPortInternal().split(":")[1];
481 //Get the external IP address and the port from the model
482 NAPTEntryEvent.Protocol proto = protocolType.getProtocol().toString().equals(ProtocolTypes.TCP.toString())
483 ? NAPTEntryEvent.Protocol.TCP : NAPTEntryEvent.Protocol.UDP;
484 IpPortExternal ipPortExternal = NatUtil.getExternalIpPortMap(dataBroker, routerId,
485 internalIpAddress, intportnum, proto);
486 if (ipPortExternal == null) {
487 LOG.debug("External Ipport mapping is not found for internalIp {} with port {}", internalIpAddress, intportnum);
490 String externalIpAddress = ipPortExternal.getIpAddress();
491 Integer extportNumber = ipPortExternal.getPortNum();
492 LOG.debug("ExternalIPport {}:{} mapping for internal ipport {}:{}",externalIpAddress,extportNumber,
493 internalIpAddress,intportnum);
495 SessionAddress sourceAddress = new SessionAddress(internalIpAddress,Integer.valueOf(intportnum));
496 SessionAddress externalAddress = new SessionAddress(externalIpAddress,extportNumber);
498 //checking naptSwitch status before installing flows
499 if(getSwitchStatus(newNaptSwitch)) {
500 //Install the flow in newNaptSwitch Outbound NAPT table.
502 NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.OUTBOUND_NAPT_TABLE,
503 vpnId, routerId, bgpVpnId, sourceAddress, externalAddress, proto);
504 } catch (Exception ex) {
505 LOG.error("Failed to add flow in OUTBOUND_NAPT_TABLE for routerid {} dpnId {} ipport {}:{} proto {}" +
506 "extIpport {}:{} BgpVpnId {} - {}", routerId, newNaptSwitch, internalIpAddress
507 , intportnum, proto, externalAddress, extportNumber,bgpVpnId,ex);
510 LOG.debug("Successfully installed a flow in SecondarySwitch {} Outbound NAPT table for router {} " +
511 "ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}", newNaptSwitch,routerId, internalIpAddress
512 , intportnum, proto, externalAddress, extportNumber,bgpVpnId);
513 //Install the flow in newNaptSwitch Inbound NAPT table.
515 NaptEventHandler.buildAndInstallNatFlows(newNaptSwitch, NatConstants.INBOUND_NAPT_TABLE,
516 vpnId, routerId, bgpVpnId, externalAddress, sourceAddress, proto);
517 } catch (Exception ex) {
518 LOG.error("Failed to add flow in INBOUND_NAPT_TABLE for routerid {} dpnId {} extIpport{}:{} proto {} " +
519 "ipport {}:{} BgpVpnId {}", routerId, newNaptSwitch, externalAddress, extportNumber, proto,
520 internalIpAddress, intportnum,bgpVpnId);
523 LOG.debug("Successfully installed a flow in SecondarySwitch {} Inbound NAPT table for router {} " +
524 "ipport {}:{} proto {} extIpport {}:{} BgpVpnId {}", newNaptSwitch,routerId, internalIpAddress
525 , intportnum, proto, externalAddress, extportNumber,bgpVpnId);
528 LOG.error("NewNaptSwitch {} gone down while installing flows from oldNaptswitch {}",
529 newNaptSwitch,oldNaptSwitch);
537 private Long getVpnIdForRouter(Long routerId) {
540 Uuid networkId = NatUtil.getNetworkIdFromRouterId(dataBroker, routerId);
541 if (networkId == null) {
542 LOG.debug("network is not associated to router {}", routerId);
544 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, networkId);
545 if (vpnUuid == null) {
546 LOG.debug("vpn is not associated for network {} in router {}", networkId, routerId);
548 Long vpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
550 LOG.debug("retrieved vpnId {} for router {}",vpnId,routerId);
553 LOG.debug("retrieved invalid vpn Id");
557 } catch (Exception ex){
558 LOG.debug("Exception while retrieving vpnId for router {} - {}", routerId, ex);
560 return NatConstants.INVALID_ID;
563 public boolean getSwitchStatus(BigInteger switchId){
564 NodeId nodeId = new NodeId("openflow:" + switchId);
565 LOG.debug("Querying switch with dpnId {} is up/down", nodeId);
566 InstanceIdentifier<Node> nodeInstanceId = InstanceIdentifier.builder(Nodes.class)
567 .child(Node.class, new NodeKey(nodeId)).build();
568 Optional<Node> nodeOptional = NatUtil.read(dataBroker,LogicalDatastoreType.OPERATIONAL,nodeInstanceId);
569 if (nodeOptional.isPresent()) {
570 LOG.debug("Switch {} is up", nodeId);
573 LOG.debug("Switch {} is down", nodeId);
577 public List<BucketInfo> handleGroupInPrimarySwitch() {
578 List<BucketInfo> listBucketInfo = new ArrayList<>();
579 List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
580 listActionInfoPrimary.add(new ActionInfo(ActionType.nx_resubmit,
581 new String[]{String.valueOf(NatConstants.TERMINATING_SERVICE_TABLE)}));
582 BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
583 listBucketInfo.add(bucketPrimary);
584 return listBucketInfo;
587 public List<BucketInfo> handleGroupInNeighborSwitches(BigInteger dpnId, String routerName, BigInteger naptSwitch) {
588 List<BucketInfo> listBucketInfo = new ArrayList<>();
589 String ifNamePrimary;
590 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
591 if (routerId == NatConstants.INVALID_ID) {
592 LOG.error("Invalid routerId returned for routerName {}",routerName);
593 return listBucketInfo;
595 ifNamePrimary = getTunnelInterfaceName(dpnId, naptSwitch);
596 if (ifNamePrimary != null) {
597 LOG.debug("TunnelInterface {} between ordinary switch {} and naptSwitch {}",ifNamePrimary,dpnId,naptSwitch);
598 List<ActionInfo> listActionInfoPrimary = getEgressActionsForInterface(ifNamePrimary, routerId);
599 BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
600 listBucketInfo.add(bucketPrimary);
602 LOG.debug("No TunnelInterface between ordinary switch {} and naptSwitch {}",dpnId,naptSwitch);
604 return listBucketInfo;
607 protected void installSnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
608 GroupEntity groupEntity = null;
610 long groupId = NatUtil.createGroupId(NatUtil.getGroupIdKey(routerName), idManager);
611 LOG.debug("install SnatMissEntry for groupId {} for dpnId {} for router {}", groupId, dpnId,routerName);
612 groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName,
613 GroupTypes.GroupAll, bucketInfo);
614 mdsalManager.installGroup(groupEntity);
615 LOG.debug("installed the SNAT to NAPT GroupEntity:{}", groupEntity);
616 } catch (Exception ex) {
617 LOG.error("Failed to install group for groupEntity {} : {}",groupEntity,ex);
621 private void modifySnatGroupEntry(BigInteger dpnId, List<BucketInfo> bucketInfo, String routerName) {
622 installSnatGroupEntry(dpnId,bucketInfo,routerName);
623 LOG.debug("modified SnatMissEntry for dpnId {} of router {}",dpnId,routerName);
626 protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
627 Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
628 RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
631 Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager.getTunnelInterfaceName(
632 new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId).setDestinationDpid(dstDpId)
633 .setTunnelType(tunType).build());
634 rpcResult = result.get();
635 if(!rpcResult.isSuccessful()) {
636 tunType = TunnelTypeGre.class;
637 result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
638 .setSourceDpid(srcDpId)
639 .setDestinationDpid(dstDpId)
640 .setTunnelType(tunType)
642 rpcResult = result.get();
643 if(!rpcResult.isSuccessful()) {
644 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
646 return rpcResult.getResult().getInterfaceName();
648 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
650 return rpcResult.getResult().getInterfaceName();
652 } catch (InterruptedException | ExecutionException e) {
653 LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and {} : {}",
654 srcDpId, dstDpId, e);
660 protected List<ActionInfo> getEgressActionsForInterface(String ifName, long routerId) {
661 LOG.debug("getEgressActionsForInterface called for interface {}", ifName);
662 List<ActionInfo> listActionInfo = new ArrayList<>();
664 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
665 interfaceManager.getEgressActionsForInterface(
666 new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).setTunnelKey(routerId).build());
667 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
668 if(!rpcResult.isSuccessful()) {
669 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}"
670 , ifName, rpcResult.getErrors());
672 List<Action> actions =
673 rpcResult.getResult().getAction();
674 for (Action action : actions) {
675 org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action actionClass = action.getAction();
676 if (actionClass instanceof OutputActionCase) {
677 listActionInfo.add(new ActionInfo(ActionType.output,
678 new String[] {((OutputActionCase)actionClass).getOutputAction()
679 .getOutputNodeConnector().getValue()}));
680 } else if (actionClass instanceof PushVlanActionCase) {
681 listActionInfo.add(new ActionInfo(ActionType.push_vlan, new String[] {}));
682 } else if (actionClass instanceof SetFieldCase) {
683 if (((SetFieldCase)actionClass).getSetField().getVlanMatch() != null) {
684 int vlanVid = ((SetFieldCase)actionClass).getSetField().getVlanMatch()
685 .getVlanId().getVlanId().getValue();
686 listActionInfo.add(new ActionInfo(ActionType.set_field_vlan_vid,
687 new String[] { Long.toString(vlanVid) }));
692 } catch (InterruptedException | ExecutionException e) {
693 LOG.warn("Exception when egress actions for interface {}", ifName, e);
695 return listActionInfo;
698 public boolean updateNaptSwitch(String routerName, BigInteger naptSwitchId) {
699 RouterToNaptSwitch naptSwitch = new RouterToNaptSwitchBuilder().setKey(new RouterToNaptSwitchKey(routerName))
700 .setPrimarySwitchId(naptSwitchId).build();
702 MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
703 NatUtil.buildNaptSwitchRouterIdentifier(routerName), naptSwitch);
704 } catch (Exception ex) {
705 LOG.error("Failed to write naptSwitch {} for router {} in ds",
706 naptSwitchId,routerName);
709 LOG.debug("Successfully updated naptSwitch {} for router {} in ds",
710 naptSwitchId,routerName);
714 public FlowEntity buildSnatFlowEntity(BigInteger dpId, String routerName, long groupId, long routerVpnId, int addordel) {
716 FlowEntity flowEntity;
717 List<MatchInfo> matches = new ArrayList<MatchInfo>();
718 matches.add(new MatchInfo(MatchFieldType.eth_type,
719 new long[]{ 0x0800L }));
720 matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
721 BigInteger.valueOf(routerVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
723 String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
725 if (addordel == NatConstants.ADD_FLOW) {
726 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
727 List<ActionInfo> actionsInfo = new ArrayList<ActionInfo>();
729 ActionInfo actionSetField = new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
730 BigInteger.valueOf(routerVpnId)}) ;
731 actionsInfo.add(actionSetField);
732 LOG.debug("Setting the tunnel to the list of action infos {}", actionsInfo);
733 actionsInfo.add(new ActionInfo(ActionType.group, new String[] {String.valueOf(groupId)}));
734 instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfo));
736 flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
737 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
738 NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
740 flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
741 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
742 NatConstants.COOKIE_SNAT_TABLE, matches, null);
747 public FlowEntity buildSnatFlowEntityForNaptSwitch(BigInteger dpId, String routerName, long routerVpnId, int addordel) {
749 FlowEntity flowEntity;
750 List<MatchInfo> matches = new ArrayList<>();
751 matches.add(new MatchInfo(MatchFieldType.eth_type,
752 new long[]{ 0x0800L }));
753 matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
754 BigInteger.valueOf(routerVpnId), MetaDataUtil.METADATA_MASK_VRFID }));
756 String flowRef = getFlowRefSnat(dpId, NatConstants.PSNAT_TABLE, routerName);
758 if (addordel == NatConstants.ADD_FLOW) {
759 List<InstructionInfo> instructions = new ArrayList<>();
761 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]
762 { NatConstants.OUTBOUND_NAPT_TABLE }));
764 flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
765 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
766 NatConstants.COOKIE_SNAT_TABLE, matches, instructions);
768 flowEntity = MDSALUtil.buildFlowEntity(dpId, NatConstants.PSNAT_TABLE, flowRef,
769 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, 0, 0,
770 NatConstants.COOKIE_SNAT_TABLE, matches, null);
775 private String getFlowRefSnat(BigInteger dpnId, short tableId, String routerID) {
776 return new StringBuilder().append(NatConstants.SNAT_FLOWID_PREFIX).append(dpnId).append(NatConstants.FLOWID_SEPARATOR).
777 append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
780 protected void installSnatFlows(String routerName,Long routerId,BigInteger naptSwitch,Long routerVpnId) {
782 if(routerId.equals(routerVpnId)) {
783 LOG.debug("Installing flows for router with internalvpnId");
784 //36 -> 46 ..Install flow forwarding packet to table46 from table36
785 LOG.debug("installTerminatingServiceTblEntry in naptswitch with dpnId {} for routerName {} with routerId {}",
786 naptSwitch, routerName,routerId);
787 externalRouterListener.installTerminatingServiceTblEntry(naptSwitch, routerName);
789 //Install default flows punting to controller in table 46(OutBoundNapt table)
790 LOG.debug("installOutboundMissEntry in naptswitch with dpnId {} for routerName {} with routerId {}",
791 naptSwitch, routerName, routerId);
792 externalRouterListener.createOutboundTblEntry(naptSwitch, routerId);
794 //Table 47 point to table 21 for inbound traffic
795 LOG.debug("installNaptPfibEntry in naptswitch with dpnId {} for router {}", naptSwitch, routerId);
796 externalRouterListener.installNaptPfibEntry(naptSwitch, routerId);
798 //36 -> 46 ..Install flow forwarding packet to table46 from table36
799 LOG.debug("installTerminatingServiceTblEntry in naptswitch with dpnId {} for routerName {} with BgpVpnId {}",
800 naptSwitch, routerName, routerVpnId);
801 externalRouterListener.installTerminatingServiceTblEntryWithUpdatedVpnId(naptSwitch, routerName, routerVpnId);
803 //Install default flows punting to controller in table 46(OutBoundNapt table)
804 LOG.debug("installOutboundMissEntry in naptswitch with dpnId {} for routerName {} with BgpVpnId {}",
805 naptSwitch, routerName, routerVpnId);
806 externalRouterListener.createOutboundTblEntryWithBgpVpn(naptSwitch, routerId, routerVpnId);
808 //Table 47 point to table 21 for inbound traffic
809 LOG.debug("installNaptPfibEntry in naptswitch with dpnId {} for router {} with BgpVpnId {}",
810 naptSwitch, routerId, routerVpnId);
811 externalRouterListener.installNaptPfibEntryWithBgpVpn(naptSwitch, routerId, routerVpnId);
814 String vpnName = getExtNetworkVpnName(routerId);
815 if(vpnName != null) {
816 //Table 47 point to table 21 for outbound traffic
817 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
819 LOG.debug("installNaptPfibEntry fin naptswitch with dpnId {} for BgpVpnId {}", naptSwitch, vpnId);
820 externalRouterListener.installNaptPfibEntry(naptSwitch, vpnId);
822 LOG.debug("Associated BgpvpnId not found for router {}",routerId);
825 //Install Fib entries for ExternalIps & program 36 -> 44
826 List<String> externalIps = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
827 if (externalIps != null) {
828 for (String externalIp : externalIps) {
829 LOG.debug("advToBgpAndInstallFibAndTsFlows in naptswitch id {} with vpnName {} and externalIp {}",
830 naptSwitch, vpnName, externalIp);
831 externalRouterListener.advToBgpAndInstallFibAndTsFlows(naptSwitch, NatConstants.INBOUND_NAPT_TABLE,
832 vpnName, routerId, externalIp, vpnService, fibService, bgpManager, dataBroker, LOG);
833 LOG.debug("Successfully added fib entries in naptswitch {} for router {} with external IP {}", naptSwitch,
834 routerId, externalIp);
837 LOG.debug("External Ip not found for routerId {}",routerId);
840 LOG.debug("Associated vpnName not found for router {}",routerId);
844 protected void bestEffortDeletion(long routerId,String routerName,HashMap<String,Long> externalIpLabel) {
845 List<String> newExternalIps = NatUtil.getExternalIpsForRouter(dataBroker,routerId);
846 if (newExternalIps != null && externalIpsCache != null) {
847 Set<String> originalSubnetIds = Sets.newHashSet(externalIpsCache);
848 Set<String> updatedSubnetIds = Sets.newHashSet(newExternalIps);
849 Sets.SetView<String> removeExternalIp = Sets.difference(originalSubnetIds, updatedSubnetIds);
850 if (removeExternalIp.isEmpty()) {
851 LOG.debug("No external Ip needed to be removed in bestEffortDeletion method for router {}",routerName);
854 String vpnName = getExtNetworkVpnName(routerId);
855 if (vpnName == null) {
856 LOG.debug("Vpn is not associated to externalN/w of router {}",routerName);
859 if (externalIpLabel == null || externalIpLabel.size() == 0) {
860 LOG.debug("ExternalIpLabel map is empty for router {}",routerName);
863 BigInteger naptSwitch = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
864 if (naptSwitch == null || naptSwitch.equals(BigInteger.ZERO)) {
865 LOG.debug("No naptSwitch is selected for router {}", routerName);
869 for (String externalIp : removeExternalIp) {
870 if (externalIpLabel.containsKey(externalIp)) {
871 label = externalIpLabel.get(externalIp);
872 LOG.debug("Label {} for ExternalIp {} for router {}",label,externalIp,routerName);
874 LOG.debug("Label for ExternalIp {} is not found for router {}",externalIp,routerName);
877 externalRouterListener.clearBgpRoutes(externalIp, vpnName);
878 externalRouterListener.delFibTsAndReverseTraffic(naptSwitch, routerId, externalIp, vpnName,label);
879 LOG.debug("Successfully removed fib entries in switch {} for router {} and externalIps {}",
880 naptSwitch, routerId, externalIp);
883 LOG.debug("No external IP found for router {}",routerId);