2 * Copyright © 2017 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 java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.List;
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
17 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
18 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
19 import org.opendaylight.genius.mdsalutil.ActionInfo;
20 import org.opendaylight.genius.mdsalutil.BucketInfo;
21 import org.opendaylight.genius.mdsalutil.GroupEntity;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.genius.mdsalutil.MatchInfo;
25 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
26 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
29 import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack;
30 import org.opendaylight.genius.mdsalutil.actions.ActionNxConntrack.NxCtAction;
31 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
32 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
33 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMeta;
34 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
38 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
39 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
40 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
41 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchCtState;
42 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
43 import org.opendaylight.netvirt.elanmanager.api.IElanService;
44 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
45 import org.opendaylight.netvirt.natservice.ha.NatDataUtil;
46 import org.opendaylight.netvirt.vpnmanager.api.IVpnFootprintService;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ProviderTypes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.types.rev160517.IpPrefixOrAddress;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.NxActionNatFlags;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.action.rev140421.NxActionNatRangePresent;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
61 public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService {
63 private static final Logger LOG = LoggerFactory.getLogger(VxlanGreConntrackBasedSnatService.class);
64 private final ExternalRoutersListener externalRouterListener;
65 private final IElanService elanManager;
66 private final ManagedNewTransactionRunner txRunner;
68 public VxlanGreConntrackBasedSnatService(DataBroker dataBroker, IMdsalApiManager mdsalManager,
69 ItmRpcService itmManager, OdlInterfaceRpcService odlInterfaceRpcService,
70 IdManagerService idManager, NAPTSwitchSelector naptSwitchSelector,
71 ExternalRoutersListener externalRouterListener, IElanService elanManager,
72 IInterfaceManager interfaceManager,
73 IVpnFootprintService vpnFootprintService,
74 IFibManager fibManager, NatDataUtil natDataUtil) {
75 super(dataBroker, mdsalManager, itmManager, idManager, naptSwitchSelector, odlInterfaceRpcService,
76 interfaceManager, vpnFootprintService, fibManager, natDataUtil);
77 this.externalRouterListener = externalRouterListener;
78 this.elanManager = elanManager;
79 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
83 public boolean handleSnatAllSwitch(Routers routers, BigInteger primarySwitchId, int addOrRemove) {
84 ProviderTypes extNwProviderType = NatUtil.getProviderTypefromNetworkId(dataBroker, routers.getNetworkId());
85 LOG.debug("VxlanGreConntrackBasedSnatService: handleSnatAllSwitch ProviderTypes {}", extNwProviderType);
86 if (extNwProviderType == ProviderTypes.FLAT || extNwProviderType == ProviderTypes.VLAN) {
87 LOG.debug("handleSnatAllSwitch : Skip FLAT/VLAN provider networks.");
90 return super.handleSnatAllSwitch(routers, primarySwitchId, addOrRemove);
94 public boolean handleSnat(Routers routers, BigInteger primarySwitchId, BigInteger dpnId, int addOrRemove) {
95 ProviderTypes extNwProviderType = NatUtil.getProviderTypefromNetworkId(dataBroker, routers.getNetworkId());
96 LOG.debug("VxlanGreConntrackBasedSnatService: handleSnat ProviderTypes {}", extNwProviderType);
97 if (extNwProviderType == ProviderTypes.FLAT || extNwProviderType == ProviderTypes.VLAN) {
98 LOG.debug("handleSnat : Skip FLAT/VLAN provider networks.");
101 return super.handleSnat(routers, primarySwitchId, dpnId, addOrRemove);
105 protected void installSnatSpecificEntriesForNaptSwitch(Routers routers, BigInteger dpnId, int addOrRemove) {
106 LOG.info("installSnatSpecificEntriesForNaptSwitch for router {}",
107 routers.getRouterName());
108 String routerName = routers.getRouterName();
109 Long routerId = NatUtil.getVpnId(dataBroker, routerName);
110 int elanId = NatUtil.getElanInstanceByName(routers.getNetworkId().getValue(), dataBroker)
111 .getElanTag().intValue();
112 /* Install Outbound NAT entries */
114 installSnatMissEntryForPrimrySwch(dpnId, routerId, elanId, addOrRemove);
115 installTerminatingServiceTblEntryForVxlanGre(dpnId, routerName, routerId, elanId, addOrRemove);
116 //Long extNetVpnId = NatUtil.getNetworkVpnIdFromRouterId(dataBroker, routerId);
117 Uuid vpnUuid = NatUtil.getVpnIdfromNetworkId(dataBroker, routers.getNetworkId());
118 if (vpnUuid == null) {
119 LOG.error("installSnatSpecificEntriesForNaptSwitch: Unable to retrieve external vpn_id for "
120 + "external network {} with routerId {}", routers.getNetworkId(), routerId);
123 Long extNetVpnId = NatUtil.getVpnId(dataBroker, vpnUuid.getValue());
124 /*//Long extNetVpnId = NatUtil.getAssociatedVPN(dataBroker, routers.getNetworkId(), LOG);
125 if (extNetVpnId == NatConstants.INVALID_ID && addOrRemove == NwConstants.ADD_FLOW) {
126 LOG.error("installSnatSpecificEntriesForNaptSwitch: Unable to retrieve external vpn_id for "
127 + "external network {} with routerId {}", routers.getNetworkId(), routerId);
130 LOG.info("installSnatSpecificEntriesForNaptSwitch: external network vpn_id {} for router {}",
131 extNetVpnId, routers.getRouterName());
132 List<ExternalIps> externalIps = routers.getExternalIps();
133 createOutboundTblTrackEntryForVxlanGre(dpnId, routerId, extNetVpnId, addOrRemove);
134 createOutboundTblEntryForVxlanGre(dpnId, routerId, extNetVpnId, externalIps, elanId, addOrRemove);
135 installNaptPfibFlowForVxlanGre(routers, dpnId, extNetVpnId, addOrRemove);
136 installNaptPfibEntry(dpnId, routerId, addOrRemove);
138 //Install Inbound NAT entries
139 installInboundEntryForVxlanGre(dpnId, routerId, extNetVpnId, externalIps, elanId, addOrRemove);
140 if (externalIps.isEmpty()) {
141 LOG.error("installSnatSpecificEntriesForNaptSwitch: No externalIP present for router {}",
145 //The logic now handle only one external IP per router, others if present will be ignored.
146 String externalIp = NatUtil.validateAndAddNetworkMask(externalIps.get(0).getIpAddress());
147 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
148 if (addOrRemove == NwConstants.ADD_FLOW) {
149 externalRouterListener.handleSnatReverseTraffic(dpnId, routers, routerId, routerName, externalIp, tx);
151 externalRouterListener.clearFibTsAndReverseTraffic(dpnId, routerId, routers.getNetworkId(),
152 Collections.singletonList(externalIp), null, routers.getExtGwMacAddress(), tx);
154 }), LOG, "Error installing SNAT-specific entries for NAPT switch");
157 protected void createOutboundTblTrackEntryForVxlanGre(BigInteger dpnId, Long routerId, Long extNetVpnId,
159 LOG.info("createOutboundTblTrackEntryForVxlanGre: Install Outbound tracking table flow on dpId {} for "
160 + "routerId {}", dpnId, routerId);
161 List<MatchInfoBase> matches = new ArrayList<>();
162 matches.add(MatchEthernetType.IPV4);
163 matches.add(new NxMatchCtState(SNAT_CT_STATE, SNAT_CT_STATE_MASK));
164 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
166 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
167 if (addOrRemove == NwConstants.ADD_FLOW) {
168 ActionSetFieldMeta actionSetFieldMeta = new ActionSetFieldMeta(MetaDataUtil
169 .getVpnIdMetadata(extNetVpnId));
170 listActionInfo.add(actionSetFieldMeta);
172 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
173 listActionInfo.add(new ActionNxResubmit(NwConstants.NAPT_PFIB_TABLE));
174 instructionInfo.add(new InstructionApplyActions(listActionInfo));
176 String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
178 syncFlow(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef,
179 NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo, addOrRemove);
183 protected void createOutboundTblEntryForVxlanGre(BigInteger dpnId, long routerId, Long extNetVpnId,
184 List<ExternalIps> externalIps, int elanId, int addOrRemove) {
185 LOG.info("createOutboundTblEntryForVxlanGre: Install Outbound table flow on dpId {} for routerId {}", dpnId,
187 List<MatchInfoBase> matches = new ArrayList<>();
188 matches.add(MatchEthernetType.IPV4);
189 matches.add(new NxMatchCtState(TRACKED_NEW_CT_STATE, TRACKED_NEW_CT_MASK));
190 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
191 if (externalIps.isEmpty()) {
192 LOG.error("createOutboundTblEntryForVxlanGre: No externalIP present for routerId {}",
196 //The logic now handle only one external IP per router, others if present will be ignored.
197 String externalIp = externalIps.get(0).getIpAddress();
198 List<ActionInfo> actionsInfos = new ArrayList<>();
199 if (addOrRemove == NwConstants.ADD_FLOW) {
200 ActionSetFieldMeta actionSetFieldMeta = new ActionSetFieldMeta(MetaDataUtil
201 .getVpnIdMetadata(extNetVpnId));
202 actionsInfos.add(actionSetFieldMeta);
204 List<ActionNxConntrack.NxCtAction> ctActionsListCommit = new ArrayList<>();
205 int rangePresent = NxActionNatRangePresent.NXNATRANGEIPV4MIN.getIntValue();
206 int flags = NxActionNatFlags.NXNATFSRC.getIntValue();
207 ActionNxConntrack.NxCtAction nxCtActionCommit = new ActionNxConntrack.NxNat(0, flags, rangePresent,
208 new IpPrefixOrAddress(externalIp.toCharArray()).getIpAddress(),
210 ctActionsListCommit.add(nxCtActionCommit);
211 int ctCommitFlag = 1;
212 ActionNxConntrack actionNxConntrackSubmit = new ActionNxConntrack(ctCommitFlag, 0, elanId,
213 NwConstants.NAPT_PFIB_TABLE, ctActionsListCommit);
214 actionsInfos.add(actionNxConntrackSubmit);
215 List<InstructionInfo> instructions = new ArrayList<>();
216 instructions.add(new InstructionApplyActions(actionsInfos));
217 String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
218 syncFlow(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef, NatConstants.SNAT_NEW_FLOW_PRIORITY,
219 flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
222 protected void installNaptPfibFlowForVxlanGre(Routers routers, BigInteger dpnId, Long extNetVpnId,
224 LOG.info("installNaptPfibFlowForVxlanGre: Install Napt preFibFlow on dpId {} with matching extNetVpnId {} "
225 + "for router {}", dpnId, extNetVpnId, routers.getRouterName());
226 List<MatchInfoBase> matches = new ArrayList<>();
227 matches.add(MatchEthernetType.IPV4);
228 if (addOrRemove == NwConstants.ADD_FLOW) {
229 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extNetVpnId),
230 MetaDataUtil.METADATA_MASK_VRFID));
232 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
233 ArrayList<InstructionInfo> instructions = new ArrayList<>();
234 listActionInfo.add(new ActionNxLoadInPort(BigInteger.ZERO));
235 listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
236 instructions.add(new InstructionApplyActions(listActionInfo));
237 String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, extNetVpnId);
238 syncFlow(dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY,
239 flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
242 protected void installInboundEntryForVxlanGre(BigInteger dpnId, long routerId, Long extNeVpnId,
243 List<ExternalIps> externalIps, int elanId, int addOrRemove) {
244 LOG.info("installInboundEntryForVxlanGre: Install Inbound table entry on dpId {} for routerId {}",
246 List<MatchInfoBase> matches = new ArrayList<>();
247 matches.add(MatchEthernetType.IPV4);
248 if (externalIps.isEmpty()) {
249 LOG.error("installInboundEntryForVxlanGre : createInboundTblEntry no externalIP present for routerId {}",
253 String externalIp = externalIps.get(0).getIpAddress();
254 matches.add(new MatchIpv4Destination(externalIp,"32"));
255 if (addOrRemove == NwConstants.ADD_FLOW) {
256 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extNeVpnId),
257 MetaDataUtil.METADATA_MASK_VRFID));
259 List<ActionInfo> actionsInfos = new ArrayList<>();
260 List<ActionNxConntrack.NxCtAction> ctActionsList = new ArrayList<>();
261 ActionNxConntrack.NxCtAction nxCtAction = new ActionNxConntrack.NxNat(0, 0, 0,null, null,0, 0);
262 ActionSetFieldMeta actionSetFieldMeta = new ActionSetFieldMeta(MetaDataUtil
263 .getVpnIdMetadata(routerId));
264 actionsInfos.add(actionSetFieldMeta);
265 ctActionsList.add(nxCtAction);
266 ActionNxConntrack actionNxConntrack = new ActionNxConntrack(0, 0, elanId, NwConstants
267 .NAPT_PFIB_TABLE,ctActionsList);
269 actionsInfos.add(actionNxConntrack);
270 List<InstructionInfo> instructions = new ArrayList<>();
271 instructions.add(new InstructionApplyActions(actionsInfos));
272 String flowRef = getFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
273 syncFlow(dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
274 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
277 protected void installTerminatingServiceTblEntryForVxlanGre(BigInteger dpnId, String routerName,
278 Long routerId, int elanId, int addOrRemove) {
279 LOG.info("installTerminatingServiceTblEntryForVxlanGre : creating entry for"
280 + "Terminating Service Table for switch {}, routerId {}", dpnId, routerId);
281 List<MatchInfo> matches = new ArrayList<>();
282 matches.add(MatchEthernetType.IPV4);
284 BigInteger tunnelId = BigInteger.valueOf(routerId);
285 if (elanManager.isOpenStackVniSemanticsEnforced()) {
286 tunnelId = NatOverVxlanUtil.getRouterVni(idManager, routerName, routerId);
288 matches.add(new MatchTunnelId(tunnelId));
290 List<ActionInfo> actionsInfos = new ArrayList<>();
291 List<NxCtAction> ctActionsList = new ArrayList<>();
292 NxCtAction nxCtAction = new ActionNxConntrack.NxNat(0, 0, 0,null, null,0, 0);
293 ctActionsList.add(nxCtAction);
294 ActionNxConntrack actionNxConntrack = new ActionNxConntrack(0, 0, elanId, NwConstants
295 .OUTBOUND_NAPT_TABLE,ctActionsList);
296 ActionSetFieldMeta actionSetFieldMeta = new ActionSetFieldMeta(MetaDataUtil
297 .getVpnIdMetadata(routerId.longValue()));
298 actionsInfos.add(actionSetFieldMeta);
299 actionsInfos.add(actionNxConntrack);
300 List<InstructionInfo> instructions = new ArrayList<>();
301 instructions.add(new InstructionApplyActions(actionsInfos));
302 String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
303 syncFlow(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
304 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
308 protected void installSnatMissEntry(BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId,
310 LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
311 List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
312 String ifNamePrimary = getTunnelInterfaceName(dpnId, primarySwitchId);
313 List<BucketInfo> listBucketInfo = new ArrayList<>();
314 if (ifNamePrimary != null) {
315 LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
316 listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
317 interfaceManager, ifNamePrimary, routerId, true);
319 BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
320 listBucketInfo.add(0, bucketPrimary);
321 LOG.debug("installSnatMissEntry : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId,
322 listBucketInfo.get(0));
323 // Install the select group
324 long groupId = createGroupId(getGroupIdKey(routerName));
325 GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
327 LOG.debug("installSnatMissEntry : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
328 mdsalManager.installGroup(groupEntity);
329 // Install miss entry pointing to group
330 LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
331 dpnId, routerName, groupId);
332 List<MatchInfo> matches = new ArrayList<>();
333 matches.add(new MatchEthernetType(0x0800L));
334 matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
336 List<ActionInfo> actionsInfo = new ArrayList<>();
338 BigInteger tunnelId = BigInteger.valueOf(routerId);
339 if (elanManager.isOpenStackVniSemanticsEnforced()) {
340 tunnelId = NatOverVxlanUtil.getRouterVni(idManager, routerName, routerId);
343 actionsInfo.add(new ActionSetFieldTunnelId(tunnelId));
344 LOG.debug("AbstractSnatService : Setting the tunnel to the list of action infos {}", actionsInfo);
345 actionsInfo.add(new ActionGroup(groupId));
346 List<InstructionInfo> instructions = new ArrayList<>();
347 instructions.add(new InstructionApplyActions(actionsInfo));
348 String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
349 syncFlow(dpnId, NwConstants.PSNAT_TABLE, flowRef, NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
350 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);