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