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