2 * Copyright (c) 2018 Red Hat, Inc. 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 static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_END;
11 import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_START;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.infra.Datastore.Configuration;
22 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
23 import org.opendaylight.genius.mdsalutil.ActionInfo;
24 import org.opendaylight.genius.mdsalutil.InstructionInfo;
25 import org.opendaylight.genius.mdsalutil.MatchInfo;
26 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
27 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
28 import org.opendaylight.genius.mdsalutil.NWUtil;
29 import org.opendaylight.genius.mdsalutil.NwConstants;
30 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
31 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
32 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
33 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
34 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
36 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
37 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
38 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
39 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Destination;
40 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
41 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
42 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
47 import org.opendaylight.yangtools.yang.common.Uint32;
48 import org.opendaylight.yangtools.yang.common.Uint64;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
53 public class Ipv6SubnetFlowProgrammer {
54 private static final Logger LOG = LoggerFactory.getLogger(Ipv6SubnetFlowProgrammer.class);
55 protected final DataBroker dataBroker;
56 protected final IMdsalApiManager mdsalManager;
59 public Ipv6SubnetFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager) {
60 this.dataBroker = dataBroker;
61 this.mdsalManager = mdsalManager;
64 public void addSubnetSpecificFlows(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
65 Uint32 routerId, Routers routers, Uint64 routerMetadata) {
66 String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routers.getRouterName());
67 for (ExternalIps externalIp : routers.getExternalIps()) {
68 if (NWUtil.isIpv4Address(externalIp.getIpAddress())) {
69 // Skip ipv4 subnets in the external network
73 // Currently we only handle one external IPv6 address per router, others if present will be ignored.
74 Uint32 extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
76 Uint64 extIpv6SubnetMetadata = MetaDataUtil.getVpnIdMetadata(extSubnetId.longValue());
77 LOG.info("addSubnetSpecificFlows : flows on NAPTSwitch {} for routerId {}, routerName {},"
78 + " extIPv6Address {} Installing", dpnId, routerId, routers.getRouterName(),
79 externalIp.getIpAddress());
81 // Program flows to handle ingress traffic coming over the tunnel port (i.e., from tableId 36 to 44)
82 addIpv6InboundTerminatingServiceTblEntry(confTx, extSubnetId, extIpv6SubnetMetadata, dpnId, routerId);
84 // Program flows in OUTBOUND_NAPT_TABLE(46) with action to send packets to NAPT_PFIB_TABLE(47)
85 addIPv6FlowToUpdateSrcMacToRouterGwMac(confTx, extGwMacAddress, extSubnetId, dpnId, routerId,
88 for (Uuid subnetId : routers.getSubnetIds()) {
89 String tenantSubnetCidr = NatUtil.getSubnetIp(dataBroker, subnetId);
90 if (!NatUtil.isIPv6Subnet(tenantSubnetCidr)) {
91 // Skip ipv4 subnets in the tenant network
95 LOG.info("addSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
96 + " tenantSubnetCidr {}, Installing",
97 dpnId, routers.getRouterName(), tenantSubnetCidr);
99 // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (egress direction)
100 addIpv6NaptPfibOutboundFlow(confTx, tenantSubnetCidr, extIpv6SubnetMetadata, dpnId, routerId);
101 // Program flows from FIB_TABLE(21) to INBOUND_NAPT_TABLE(44) (ingress direction)
102 addIpv6NaptInboundFibEntry(confTx, extSubnetId, tenantSubnetCidr, extIpv6SubnetMetadata,
104 // Program flows from INBOUND_NAPT_TABLE(44) to NAPT_PFIB_TABLE(47) (ingress direction)
105 addIpv6NaptInboundNaptFlow(confTx, extSubnetId, tenantSubnetCidr, extIpv6SubnetMetadata,
106 dpnId, routerId, routerMetadata);
111 public void removeSubnetSpecificFlows(TypedReadWriteTransaction<Configuration> confTx, Uint64 dpnId,
112 Uint32 routerId, Routers routers)
113 throws ExecutionException, InterruptedException {
114 for (ExternalIps externalIp : routers.getExternalIps()) {
115 if (NWUtil.isIpv4Address(externalIp.getIpAddress())) {
116 // Skip ipv4 subnets in the external network
119 // Currently we only handle one external IPv6 address per router, others if present will be ignored.
120 LOG.info("removeSubnetSpecificFlows : flows on NAPTSwitch {} for routerId {}, routerName {},"
121 + " extIPv6Address {}, Removing",
122 dpnId, routerId, routers.getRouterName(), externalIp.getIpAddress());
124 // Program flows to handle ingress traffic coming over the tunnel port (i.e., from tableId 36 to 44)
125 removeIpv6InboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
127 // Program flows in OUTBOUND_NAPT_TABLE(46) with action to send packets to NAPT_PFIB_TABLE(47)
128 removeIPv6FlowToUpdateSrcMacToRouterGwMac(confTx, dpnId, routerId);
130 for (Uuid subnetId : routers.getSubnetIds()) {
131 String tenantSubnetCidr = NatUtil.getSubnetIp(dataBroker, subnetId);
132 if (!NatUtil.isIPv6Subnet(tenantSubnetCidr)) {
133 // Skip ipv4 subnets in the tenant network
137 LOG.info("removeSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
138 + " tenantSubnetCidr {}, Removing",
139 dpnId, routers.getRouterName(), tenantSubnetCidr);
141 // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (egress direction)
142 removeIpv6NaptPfibOutboundFlow(confTx, tenantSubnetCidr, dpnId, routerId);
143 // Program flows from FIB_TABLE(21) to INBOUND_NAPT_TABLE(44) (ingress direction)
144 removeIpv6NaptInboundFibEntry(confTx, tenantSubnetCidr, dpnId, routerId);
145 // Program flows from INBOUND_NAPT_TABLE(44) to NAPT_PFIB_TABLE(47) (ingress direction)
146 removeIpv6NaptInboundNaptFlow(confTx, tenantSubnetCidr, dpnId, routerId);
151 private void addIpv6InboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
152 Uint32 extSubnetId, Uint64 extIpv6SubnetMetadata,Uint64 dpnId,
154 // Install the tunnel table entry in NAPT Switch for inbound traffic from a non NAPT Switch.
155 LOG.debug("addIpv6InboundTerminatingServiceTblEntry : entry for Terminating Service Table for switch {},"
156 + " routerId {}, Installing", dpnId, routerId);
157 List<MatchInfo> matches = new ArrayList<>();
158 matches.add(MatchEthernetType.IPV6);
160 List<ActionInfo> actionsInfos = new ArrayList<>();
161 if (extSubnetId == NatConstants.INVALID_ID) {
162 LOG.error("addIpv6InboundTerminatingServiceTblEntry : external subnet id is invalid.");
165 matches.add(new MatchTunnelId(Uint64.valueOf(extSubnetId)));
166 ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(extIpv6SubnetMetadata, LOAD_START, LOAD_END);
167 actionsInfos.add(actionLoadMeta);
168 actionsInfos.add(new ActionNxResubmit(NwConstants.INBOUND_NAPT_TABLE));
169 List<InstructionInfo> instructions = new ArrayList<>();
170 instructions.add(new InstructionApplyActions(actionsInfos));
172 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
173 flowRef = flowRef + ".Inbound";
174 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
175 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
176 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
179 private void removeIpv6InboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
180 Uint64 dpnId, Uint32 routerId) throws ExecutionException, InterruptedException {
181 // Install the tunnel table entry in NAPT Switch for inbound traffic from a non NAPT Switch.
182 LOG.debug("removeIpv6InboundTerminatingServiceTblEntry : entry for Terminating Service Table for switch {},"
183 + " routerId {}, Removing", dpnId, routerId);
184 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
185 flowRef = flowRef + ".Inbound";
186 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
189 private void addIPv6FlowToUpdateSrcMacToRouterGwMac(TypedReadWriteTransaction<Configuration> confTx,
190 String extGwMacAddress, Uint32 extSubnetId, Uint64 dpnId, Uint32 routerId, Uint64 routerMetadata) {
191 LOG.debug("addIPv6FlowToUpdateSrcMacToRouterGwMac : called for switch {}, routerId {}", dpnId, routerId);
192 List<MatchInfoBase> matches = new ArrayList<>();
193 matches.add(MatchEthernetType.IPV6);
194 matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
196 ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
197 listActionInfo.add(new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress)));
198 ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
199 .getVpnIdMetadata(extSubnetId.longValue()), LOAD_START, LOAD_END);
200 listActionInfo.add(actionLoadMeta);
201 ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
202 listActionInfo.add(new ActionNxResubmit(NwConstants.NAPT_PFIB_TABLE));
203 instructionInfo.add(new InstructionApplyActions(listActionInfo));
205 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
206 flowRef += ".Outbound";
207 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
208 NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef,
209 NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
212 private void removeIPv6FlowToUpdateSrcMacToRouterGwMac(TypedReadWriteTransaction<Configuration> confTx,
213 Uint64 dpnId, Uint32 routerId)
214 throws ExecutionException, InterruptedException {
215 LOG.debug("removeIPv6FlowToUpdateSrcMacToRouterGwMac : called for switch {}, routerId {}", dpnId, routerId);
216 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
217 flowRef += ".Outbound";
218 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
221 private void addIpv6NaptPfibOutboundFlow(TypedReadWriteTransaction<Configuration> confTx,
222 String tenantIpv6SubnetCidr, Uint64 extIpv6SubnetMetadata, Uint64 dpnId, Uint32 routerId) {
223 LOG.debug("addIpv6NaptPfibOutboundFlow : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
224 dpnId, routerId, tenantIpv6SubnetCidr);
225 List<MatchInfoBase> matches = new ArrayList<>();
226 matches.add(MatchEthernetType.IPV6);
227 matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
228 matches.add(new MatchIpv6Source(tenantIpv6SubnetCidr));
230 List<ActionInfo> listActionInfo = new ArrayList<>();
231 ArrayList<InstructionInfo> instructions = new ArrayList<>();
232 listActionInfo.add(new ActionNxLoadInPort(Uint64.ZERO));
233 listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
234 instructions.add(new InstructionApplyActions(listActionInfo));
236 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
237 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Outbound";
238 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
239 NatConstants.SNAT_TRK_FLOW_PRIORITY,
240 flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
243 private void removeIpv6NaptPfibOutboundFlow(TypedReadWriteTransaction<Configuration> confTx,
244 String tenantIpv6SubnetCidr, Uint64 dpnId, Uint32 routerId)
245 throws ExecutionException, InterruptedException {
246 LOG.debug("removeIpv6NaptPfibOutboundFlow : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
247 dpnId, routerId, tenantIpv6SubnetCidr);
248 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
249 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Outbound";
250 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
253 private void addIpv6NaptInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx, Uint32 extSubnetId,
254 String tenantIpv6SubnetCidr, Uint64 extIpv6SubnetMetadata, Uint64 dpnId, Uint32 routerId) {
255 LOG.debug("addIpv6NaptInboundFibEntry : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
256 dpnId, routerId, tenantIpv6SubnetCidr);
257 List<MatchInfo> matches = new ArrayList<>();
258 matches.add(MatchEthernetType.IPV6);
259 if (extSubnetId == NatConstants.INVALID_ID) {
260 LOG.error("installIpv6NaptInboundFibEntry: external subnet id is invalid.");
263 matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
264 matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
266 List<InstructionInfo> instructions = new ArrayList<>();
267 instructions.add(new InstructionGotoTable(NwConstants.INBOUND_NAPT_TABLE));
269 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
270 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
271 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
272 NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
273 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
276 private void removeIpv6NaptInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx,
277 String tenantIpv6SubnetCidr, Uint64 dpnId, Uint32 routerId)
278 throws ExecutionException, InterruptedException {
279 LOG.debug("removeIpv6NaptInboundFibEntry : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
280 dpnId, routerId, tenantIpv6SubnetCidr);
281 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
282 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
283 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
286 private void addIpv6NaptInboundNaptFlow(TypedReadWriteTransaction<Configuration> confTx, Uint32 extSubnetId,
287 String tenantIpv6SubnetCidr, Uint64 extIpv6SubnetMetadata, Uint64 dpnId, Uint32 routerId,
288 Uint64 routerMetadata) {
289 LOG.debug("addIpv6NaptInboundNaptFlow : called for switch {}, routerId {}, tenantIPv6Cidr {}",
290 dpnId, routerId, tenantIpv6SubnetCidr);
291 List<MatchInfoBase> matches = new ArrayList<>();
292 matches.add(MatchEthernetType.IPV6);
293 if (extSubnetId == NatConstants.INVALID_ID) {
294 LOG.error("installIpv6NaptInboundNaptFlow : external subnet id is invalid.");
297 matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
299 matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
301 List<InstructionInfo> instructions = new ArrayList<>();
302 instructions.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
303 instructions.add(new InstructionWriteMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
305 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
306 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
307 NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef,
308 NatConstants.DEFAULT_TS_FLOW_PRIORITY,
309 flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
312 private void removeIpv6NaptInboundNaptFlow(TypedReadWriteTransaction<Configuration> confTx,
313 String tenantIpv6SubnetCidr,Uint64 dpnId, Uint32 routerId)
314 throws ExecutionException, InterruptedException {
315 LOG.debug("removeIpv6NaptInboundNaptFlow : called for switch {}, routerId {}, tenantIPv6Cidr {}",
316 dpnId, routerId, tenantIpv6SubnetCidr);
317 String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
318 flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
319 NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);