22945dd33666228189927f2b9dc731637aa161de
[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.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.ExecutionException;
16
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19
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;
51
52 @Singleton
53 public class Ipv6SubnetFlowProgrammer {
54     private static final Logger LOG = LoggerFactory.getLogger(Ipv6SubnetFlowProgrammer.class);
55     protected final DataBroker dataBroker;
56     protected final IMdsalApiManager mdsalManager;
57
58     @Inject
59     public Ipv6SubnetFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager) {
60         this.dataBroker = dataBroker;
61         this.mdsalManager = mdsalManager;
62     }
63
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
70                 continue;
71             }
72
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());
75
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());
80
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);
83
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,
86                     routerMetadata);
87
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
92                     continue;
93                 }
94
95                 LOG.info("addSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
96                         + " tenantSubnetCidr {}, Installing",
97                         dpnId, routers.getRouterName(), tenantSubnetCidr);
98
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,
103                         dpnId, routerId);
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);
107             }
108         }
109     }
110
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
117                 continue;
118             }
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());
123
124             // Program flows to handle ingress traffic coming over the tunnel port (i.e., from tableId 36 to 44)
125             removeIpv6InboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
126
127             // Program flows in OUTBOUND_NAPT_TABLE(46) with action to send packets to NAPT_PFIB_TABLE(47)
128             removeIPv6FlowToUpdateSrcMacToRouterGwMac(confTx, dpnId, routerId);
129
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
134                     continue;
135                 }
136
137                 LOG.info("removeSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
138                         + " tenantSubnetCidr {}, Removing",
139                         dpnId, routers.getRouterName(), tenantSubnetCidr);
140
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);
147             }
148         }
149     }
150
151     private void addIpv6InboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
152                                                           Uint32 extSubnetId, Uint64 extIpv6SubnetMetadata,Uint64 dpnId,
153                                                           Uint32 routerId) {
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);
159
160         List<ActionInfo> actionsInfos = new ArrayList<>();
161         if (extSubnetId == NatConstants.INVALID_ID) {
162             LOG.error("addIpv6InboundTerminatingServiceTblEntry : external subnet id is invalid.");
163             return;
164         }
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));
171
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);
177     }
178
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);
187     }
188
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));
195
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));
204
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);
210     }
211
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);
219     }
220
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));
229
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));
235
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);
241     }
242
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);
251     }
252
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.");
261             return;
262         }
263         matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
264         matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
265
266         List<InstructionInfo> instructions = new ArrayList<>();
267         instructions.add(new InstructionGotoTable(NwConstants.INBOUND_NAPT_TABLE));
268
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);
274     }
275
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);
284     }
285
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.");
295             return;
296         }
297         matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
298
299         matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
300
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));
304
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);
310     }
311
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);
320     }
321 }