NETVIRT-1630 migrate to md-sal APIs
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / SubnetRoutePacketInHandler.java
1 /*
2  * Copyright (c) 2016, 2018 Ericsson India Global Services Pvt Ltd. 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.vpnmanager;
9
10 import com.google.common.primitives.Ints;
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.List;
14 import java.util.Optional;
15 import java.util.concurrent.ExecutionException;
16 import javax.inject.Inject;
17 import javax.inject.Singleton;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
20 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
22 import org.opendaylight.genius.mdsalutil.NWUtil;
23 import org.opendaylight.genius.mdsalutil.NwConstants;
24 import org.opendaylight.genius.mdsalutil.packet.Ethernet;
25 import org.opendaylight.genius.mdsalutil.packet.IPv4;
26 import org.opendaylight.infrautils.metrics.Counter;
27 import org.opendaylight.infrautils.metrics.Labeled;
28 import org.opendaylight.infrautils.metrics.MetricDescriptor;
29 import org.opendaylight.infrautils.metrics.MetricProvider;
30 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
31 import org.opendaylight.mdsal.binding.api.DataBroker;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
34 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
35 import org.opendaylight.netvirt.vpnmanager.iplearn.ipv4.ArpUtils;
36 import org.opendaylight.netvirt.vpnmanager.utilities.CounterUtility;
37 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
38 import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
39 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
40 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
41 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefixBuilder;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
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.interfacemanager.rev160406.IfTunnel;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagName;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.interfaces.VpnInterface;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
64 import org.opendaylight.yangtools.yang.common.Uint32;
65 import org.opendaylight.yangtools.yang.common.Uint64;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 @Singleton
70 public class SubnetRoutePacketInHandler implements PacketProcessingListener {
71     private static final Logger LOG = LoggerFactory.getLogger(SubnetRoutePacketInHandler.class);
72     private static final String LOGGING_PREFIX = "SUBNETROUTE:";
73     private final DataBroker dataBroker;
74     private final PacketProcessingService packetService;
75     private final OdlInterfaceRpcService odlInterfaceRpcService;
76     private final ICentralizedSwitchProvider centralizedSwitchProvider;
77     private final IInterfaceManager interfaceManager;
78     private final Ipv6NdUtilService ipv6NdUtilService;
79     private final Labeled<Labeled<Counter>> packetInCounter;
80     private final VpnUtil vpnUtil;
81
82     @Inject
83     public SubnetRoutePacketInHandler(final DataBroker dataBroker, final PacketProcessingService packetService,
84             final OdlInterfaceRpcService odlInterfaceRpcService,
85             final ICentralizedSwitchProvider centralizedSwitchProvider, final IInterfaceManager interfaceManager,
86             final Ipv6NdUtilService ipv6NdUtilService, MetricProvider metricProvider, VpnUtil vpnUtil) {
87         this.dataBroker = dataBroker;
88         this.packetService = packetService;
89         this.odlInterfaceRpcService = odlInterfaceRpcService;
90         this.centralizedSwitchProvider = centralizedSwitchProvider;
91         this.interfaceManager = interfaceManager;
92         this.ipv6NdUtilService = ipv6NdUtilService;
93         packetInCounter =  metricProvider.newCounter(MetricDescriptor.builder().anchor(this)
94                 .project(CounterUtility.getProject()).module(CounterUtility.getModule())
95                 .id(CounterUtility.getSubnetRouteId()).build(), "action","sourceIp.destinationIp");
96         this.vpnUtil = vpnUtil;
97     }
98
99     @Override
100     public void onPacketReceived(PacketReceived notification) {
101
102         short tableId = notification.getTableId().getValue().toJava();
103         LOG.trace("{} onPacketReceived: Packet punted from table {}", LOGGING_PREFIX, tableId);
104         if (!vpnUtil.isArpLearningEnabled()) {
105             LOG.trace("Not handling packet as ARP Based Learning is disabled");
106             return;
107         }
108         byte[] data = notification.getPayload();
109         if (notification.getMatch() == null || notification.getMatch().getMetadata() == null) {
110             LOG.error("{} onPacketReceived: Received from table {} where the match or metadata are null",
111                     LOGGING_PREFIX, tableId);
112             return;
113         }
114         Uint64 metadata = notification.getMatch().getMetadata().getMetadata();
115         Ethernet res = new Ethernet();
116
117         if (tableId == NwConstants.L3_SUBNET_ROUTE_TABLE) {
118             LOG.trace("{} onPacketReceived: Some packet received as {}", LOGGING_PREFIX, notification);
119             try {
120                 res.deserialize(data, 0, data.length * Byte.SIZE);
121             } catch (PacketException e) {
122                 LOG.error("{} onPacketReceived: Failed to decode Packet ", LOGGING_PREFIX, e);
123                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
124                         .label(CounterUtility.getSubnetRouteInvalidPacket());
125                 counter.increment();
126                 return;
127             }
128             byte[] srcIpBytes = null;
129             byte[] dstIpBytes = null;
130             String srcIpStr;
131             String dstIpStr;
132             String srcMac = NWUtil.toStringMacAddress(res.getSourceMACAddress());
133             try {
134                 Packet pkt = res.getPayload();
135                 if (pkt instanceof IPv4) {
136                     IPv4 ipv4 = (IPv4) pkt;
137                     srcIpBytes = Ints.toByteArray(ipv4.getSourceAddress());
138                     dstIpBytes = Ints.toByteArray(ipv4.getDestinationAddress());
139                     srcIpStr = NWUtil.toStringIpAddress(srcIpBytes);
140                     dstIpStr = NWUtil.toStringIpAddress(dstIpBytes);
141                     Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_recived.toString())
142                             .label(srcIpStr + "." + dstIpStr);
143                     counter.increment();
144                     // It is an ARP request on a configured VPN. So we must
145                     // attempt to respond.
146                 } else {
147                     // IPv6 case
148                     // TODO: IPv6 deserializer
149                     int ethType = BitBufferHelper.getInt(
150                             BitBufferHelper.getBits(data, VpnConstants.ETHTYPE_START, VpnConstants.TWO_BYTES));
151                     if (ethType == VpnConstants.IP_V6_ETHTYPE) {
152                         srcIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 64, 128);
153                         dstIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 192, 128);
154                     }
155                 }
156                 if (srcIpBytes == null || dstIpBytes == null) {
157                     LOG.trace("{} onPacketReceived: Non-IP packet received as {}", LOGGING_PREFIX, notification);
158                     return;
159                 }
160                 handleIpPackets(srcIpBytes, dstIpBytes, NWUtil.toStringIpAddress(srcIpBytes),
161                         NWUtil.toStringIpAddress(dstIpBytes), srcMac, metadata);
162
163             } catch (InterruptedException | ExecutionException | BufferException ex) {
164                 // Failed to handle packet
165                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
166                         .label(NWUtil.toStringIpAddress(srcIpBytes) + "." + NWUtil.toStringIpAddress(srcIpBytes));
167                 counter.increment();
168                 LOG.error("{} onPacketReceived: Failed to handle subnetroute packet.", LOGGING_PREFIX, ex);
169             } catch (UnknownHostException e) {
170                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
171                         .label(CounterUtility.getSubnetRouteInvalidPacket());
172                 counter.increment();
173                 LOG.error("{} onPacketReceived: Unknown host detected while handling subnetroute", LOGGING_PREFIX, e);
174             }
175             return;
176         }
177         // All Arp responses learning for invisble IPs is handled by
178         // ArpNotificationHandler
179
180     }
181
182     private void handleIpPackets(byte[] srcIp, byte[] dstIp, String srcIpStr, String dstIpStr, String srcMac,
183             Uint64 metadata)
184             throws UnknownHostException, InterruptedException, ExecutionException {
185         Uint32 vpnId = Uint32.valueOf(MetaDataUtil.getVpnIdFromMetadata(metadata));
186
187         LOG.info("{} onPacketReceived: Processing IP Packet received with Source IP {} and Target IP {}"
188                 + " and vpnId {}", LOGGING_PREFIX, srcIpStr, dstIpStr, vpnId);
189
190         Optional<VpnIds> vpnIdsOptional = SingleTransactionDataBroker.syncReadOptional(dataBroker,
191                 LogicalDatastoreType.CONFIGURATION, VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId));
192
193         if (!vpnIdsOptional.isPresent()) {
194             // Donot trigger subnetroute logic for packets from
195             // unknown VPNs
196             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_drop.toString())
197                     .label(srcIpStr + "." + dstIpStr);
198             counter.increment();
199             LOG.info("{} onPacketReceived: Ignoring IPv4 packet with destination Ip {} and source Ip {}"
200                     + " as it came on unknown VPN with ID {}", LOGGING_PREFIX, dstIpStr, srcIpStr, vpnId);
201             return;
202         }
203
204         String vpnIdVpnInstanceName = vpnIdsOptional.get().getVpnInstanceName();
205         VpnPortipToPort persistedIP =
206                 vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnIdVpnInstanceName, dstIpStr);
207         if (persistedIP != null && !persistedIP.isLearntIp()) {
208             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_drop.toString())
209                     .label(srcIpStr + "." + dstIpStr);
210             counter.increment();
211             LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source IP {} vpnId {} "
212                     + "is a valid Neutron port,ignoring subnet route processing", LOGGING_PREFIX, dstIpStr,
213                     srcIp, vpnId);
214             return;
215         }
216
217         if (vpnUtil.getLearntVpnVipToPort(vpnIdVpnInstanceName, dstIpStr) != null) {
218             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
219                     .label(srcIpStr + "." + dstIpStr);
220             counter.increment();
221             LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source Ip {} vpnId {}"
222                     + " is an already discovered IPAddress, ignoring subnet route processing",
223                     LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
224             return;
225         }
226
227         long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
228         if (elanTag == 0L) {
229             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
230                     .label(srcIpStr + "." + dstIpStr);
231             counter.increment();
232             LOG.error("{} onPacketReceived: elanTag value from metadata found to be 0, for IP "
233                     + " Packet received with Target IP {} src Ip {} vpnId {}",
234                     LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
235             return;
236         }
237
238         if (!vpnIdsOptional.get().isExternalVpn()) {
239             handleInternalVpnSubnetRoutePacket(metadata, dstIp, srcIpStr, dstIpStr, vpnIdVpnInstanceName,
240                     elanTag);
241             return;
242         }
243
244         handleBgpVpnSubnetRoute(srcMac, dstIp, dstIpStr, srcIpStr, elanTag);
245     }
246
247     private void handleBgpVpnSubnetRoute(String srcMac, byte[] dstIp, String dstIpStr, String srcIpStr,
248             long elanTag) throws UnknownHostException {
249         LOG.info("{} handleBgpVpnSubnetRoute: Processing IP Packet received with Source IP {} and Target IP {}"
250                 + " and elan Tag {}", LOGGING_PREFIX, srcIpStr, dstIpStr, elanTag);
251         SubnetOpDataEntry targetSubnetForPacketOut =
252                 getTargetSubnetForPacketOut(elanTag, dstIpStr);
253         if (targetSubnetForPacketOut != null) {
254             // Handle subnet routes ip requests
255             transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(),
256                                         srcIpStr, srcMac, dstIp, dstIpStr, elanTag);
257         } else {
258             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
259                     .label(srcIpStr + "." + dstIpStr);
260             counter.increment();
261             LOG.debug("{} handleBgpVpnSubnetRoute: Could not find target subnet for packet out {}", LOGGING_PREFIX,
262                     dstIpStr);
263         }
264     }
265
266     private void handleInternalVpnSubnetRoutePacket(Uint64 metadata, byte[] dstIp, String srcIpStr, String dstIpStr,
267             String vpnIdVpnInstanceName, long elanTag)
268             throws InterruptedException, ExecutionException, UnknownHostException {
269         String vmVpnInterfaceName = vpnUtil.getVpnInterfaceName(metadata);
270         if (isTunnel(vmVpnInterfaceName)) {
271             handlePacketFromTunnelToExternalNetwork(vpnIdVpnInstanceName, srcIpStr, dstIp, dstIpStr, elanTag);
272         }
273         VpnInterface vmVpnInterface = vpnUtil.getVpnInterface(vmVpnInterfaceName);
274         if (vmVpnInterface == null) {
275             LOG.error("Vpn interface {} doesn't exist.", vmVpnInterfaceName);
276             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
277                     .label(srcIpStr + "." + dstIpStr);
278             counter.increment();
279             return;
280         }
281         if (VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnIdVpnInstanceName,
282                vmVpnInterface.getVpnInstanceNames())
283                && !vpnUtil.isBgpVpnInternet(vpnIdVpnInstanceName)) {
284             LOG.trace("Unknown IP is in internal network");
285             handlePacketToInternalNetwork(dstIp, dstIpStr, elanTag, srcIpStr);
286         } else {
287             LOG.trace("Unknown IP is in external network");
288             String vpnName = vpnUtil.getInternetVpnFromVpnInstanceList(vmVpnInterface.getVpnInstanceNames());
289             if (vpnName != null) {
290                 handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
291             } else {
292                 vpnName = VpnHelper.getFirstVpnNameFromVpnInterface(vmVpnInterface);
293                 LOG.trace("Unknown IP is in external network, but internet VPN not found." + " fallback to first VPN");
294                 handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
295
296             }
297         }
298     }
299
300     private void transmitArpOrNsPacket(Uint64 dpnId, String sourceIpAddress, String sourceMac, byte[] dstIpBytes,
301             String dstIpAddress, long elanTag) throws UnknownHostException {
302         long groupid = VpnUtil.getRemoteBCGroup(elanTag);
303         if (NWUtil.isIpv4Address(dstIpAddress)) {
304             LOG.debug("Sending ARP: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}, groupid={}", sourceIpAddress,
305                     sourceMac, dstIpAddress, dpnId, elanTag, groupid);
306             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_arp_sent.toString())
307                     .label(sourceIpAddress + "." + dstIpAddress);
308             counter.increment();
309
310             TransmitPacketInput packetInput =
311                     ArpUtils.createArpRequestInput(dpnId, groupid, HexEncode.bytesFromHexString(sourceMac),
312                             InetAddress.getByName(sourceIpAddress).getAddress(), dstIpBytes);
313             JdkFutures.addErrorLogging(packetService.transmitPacket(packetInput), LOG, "Transmit packet");
314         } else {
315             // IPv6 case
316             LOG.debug("Sending NS: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}, groupid={}", sourceIpAddress,
317                     sourceMac, dstIpAddress, dpnId, elanTag, groupid);
318             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_ns_sent.toString())
319                     .label(sourceIpAddress + "." + dstIpAddress);
320             counter.increment();
321
322             VpnUtil.sendNeighborSolicationToOfGroup(this.ipv6NdUtilService, new Ipv6Address(sourceIpAddress),
323                     new MacAddress(sourceMac), new Ipv6Address(dstIpAddress), groupid, dpnId);
324         }
325     }
326
327     private void handlePacketToInternalNetwork(byte[] dstIp, String dstIpStr, long elanTag, String srcIpStr)
328             throws UnknownHostException {
329         try {
330             SubnetOpDataEntry targetSubnetForPacketOut =
331                     getTargetSubnetForPacketOut(elanTag, dstIpStr);
332
333             if (targetSubnetForPacketOut == null) {
334                 LOG.debug("Couldn't find matching subnet for elan tag {} and destination ip {}", elanTag, dstIpStr);
335                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
336                         .label(srcIpStr + "." + dstIpStr);
337                 counter.increment();
338                 return;
339             }
340
341             Optional<Subnetmap> subnetMap = SingleTransactionDataBroker.syncReadOptional(dataBroker,
342                     LogicalDatastoreType.CONFIGURATION,
343                     VpnUtil.buildSubnetmapIdentifier(targetSubnetForPacketOut.getSubnetId()));
344             if (!subnetMap.isPresent()) {
345                 LOG.debug("Couldn't find subnet map for subnet {}", targetSubnetForPacketOut.getSubnetId());
346                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
347                         .label(srcIpStr + "." + dstIpStr);
348                 counter.increment();
349                 return;
350             }
351
352             String sourceIp = subnetMap.get().getRouterInterfaceFixedIp();
353             if (sourceIp == null) {
354                 LOG.debug("Subnet map {} doesn't have a router interface ip defined", subnetMap.get().getId());
355                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
356                         .label("." + dstIpStr);
357                 counter.increment();
358                 return;
359             }
360
361             String sourceMac = subnetMap.get().getRouterIntfMacAddress();
362             if (sourceMac == null) {
363                 LOG.debug("Subnet map {} doesn't have a router interface mac address defined",
364                         subnetMap.get().getId());
365                 Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
366                         .label(sourceIp + "." + dstIpStr);
367                 counter.increment();
368                 return;
369             }
370
371             transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(),
372                                         sourceIp, sourceMac, dstIp, dstIpStr, elanTag);
373         } catch (InterruptedException | ExecutionException e) {
374             LOG.error("handlePacketToInternalNetwork: Failed to read data store for destIp {} elanTag {}", dstIpStr,
375                     elanTag);
376         }
377     }
378
379     private void handlePacketFromTunnelToExternalNetwork(String vpnIdVpnInstanceName, String srcIpStr, byte[] dstIp,
380         String dstIpStr, long elanTag) throws UnknownHostException {
381         String routerId = vpnUtil.getAssociatedExternalRouter(srcIpStr);
382         if (null == routerId) {
383             LOG.debug("This ip is not associated with any external router: {}", srcIpStr);
384             return;
385         }
386         handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), routerId, dstIp, dstIpStr, elanTag);
387     }
388
389     private void handlePacketToExternalNetwork(Uuid vpnInstanceNameUuid, String routerId, byte[] dstIp, String dstIpStr,
390             long elanTag) throws UnknownHostException {
391         Routers externalRouter = vpnUtil.getExternalRouter(routerId);
392         if (externalRouter == null) {
393             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
394                     .label("." + dstIpStr);
395             counter.increment();
396             LOG.debug("{} handlePacketToExternalNetwork: Can't find external router with id {}", LOGGING_PREFIX,
397                     routerId);
398             return;
399         }
400
401         List<ExternalIps> externalIps = externalRouter.getExternalIps();
402         if (externalIps == null || externalIps.isEmpty()) {
403             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
404                     .label("." + dstIpStr);
405             counter.increment();
406             LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have any external ips.",
407                     LOGGING_PREFIX, externalRouter.getRouterName());
408             return;
409         }
410
411         java.util.Optional<ExternalIps> externalIp = externalRouter.getExternalIps().stream()
412                 .filter(eip -> vpnInstanceNameUuid.equals(eip.getSubnetId())).findFirst();
413         if (!externalIp.isPresent()) {
414             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
415                     .label(externalIp.get().getIpAddress() + "." + dstIpStr);
416             counter.increment();
417             LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have an external ip for subnet id {}.",
418                     LOGGING_PREFIX, externalRouter.getRouterName(), vpnInstanceNameUuid);
419             return;
420         }
421
422         Uint64 dpnId = centralizedSwitchProvider.getPrimarySwitchForRouter(externalRouter.getRouterName());
423         if (Uint64.ZERO.equals(dpnId)) {
424             Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_failed.toString())
425                     .label(externalIp.get().getIpAddress() + "." + dstIpStr);
426             counter.increment();
427             LOG.debug("{} handlePacketToExternalNetwork: Could not find primary switch for router {}.",
428                     LOGGING_PREFIX, externalRouter.getRouterName());
429             return;
430         }
431
432         transmitArpOrNsPacket(dpnId, externalIp.get().getIpAddress(), externalRouter.getExtGwMacAddress(), dstIp,
433                 dstIpStr, elanTag);
434
435         Counter counter = packetInCounter.label(CounterUtility.subnet_route_packet_processed.toString())
436                 .label(externalIp.get().getIpAddress() + "." + dstIpStr);
437         counter.increment();
438     }
439
440     // return only the first VPN subnetopdataentry
441     @Nullable
442     private SubnetOpDataEntry getTargetSubnetForPacketOut(long elanTag, String ipAddress) {
443         ElanTagName elanInfo = vpnUtil.getElanInfoByElanTag(elanTag);
444         if (elanInfo == null) {
445             LOG.error("{} getTargetDpnForPacketOut: Unable to retrieve ElanInfo for elanTag {}", LOGGING_PREFIX,
446                     elanTag);
447             return null;
448         }
449         try {
450             Optional<NetworkMap> optionalNetworkMap = SingleTransactionDataBroker.syncReadOptional(dataBroker,
451                     LogicalDatastoreType.CONFIGURATION, VpnUtil.buildNetworkMapIdentifier(new Uuid(
452                             elanInfo.getName())));
453             if (!optionalNetworkMap.isPresent()) {
454                 LOG.debug("{} getTargetDpnForPacketOut: No network map found for elan info {}", LOGGING_PREFIX,
455                         elanInfo.getName());
456                 return null;
457             }
458             List<Uuid> subnetList = optionalNetworkMap.get().getSubnetIdList();
459             LOG.debug("{} getTargetDpnForPacketOut: Obtained subnetList as {} for network {}", LOGGING_PREFIX,
460                     subnetList, elanInfo.getName());
461             if (subnetList != null) {
462                 for (Uuid subnetId : subnetList) {
463                     String vpnName = null;
464                     Subnetmap sn = vpnUtil.getSubnetmapFromItsUuid(subnetId);
465                     if (sn != null && sn.getVpnId() != null) {
466                         vpnName = sn.getVpnId().getValue();
467                     }
468                     if (vpnName == null) {
469                         continue;
470                     }
471                     Optional<SubnetOpDataEntry> optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
472                         LogicalDatastoreType.OPERATIONAL, VpnUtil.buildSubnetOpDataEntryInstanceIdentifier(subnetId));
473                     if (!optionalSubs.isPresent()) {
474                         continue;
475                     }
476                     SubnetOpDataEntry subOpEntry = optionalSubs.get();
477                     if (subOpEntry.getNhDpnId() != null) {
478                         LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {}", LOGGING_PREFIX,
479                             subnetId.getValue());
480                         IpPrefix cidr = IpPrefixBuilder.getDefaultInstance(subOpEntry.getSubnetCidr());
481                         boolean match = NWUtil.isIpAddressInRange(IpAddressBuilder.getDefaultInstance(ipAddress), cidr);
482                         LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {} matching {}", LOGGING_PREFIX,
483                             subnetId.getValue(), match);
484                         if (match) {
485                             return subOpEntry;
486                         }
487                     }
488                 }
489             }
490         } catch (InterruptedException | ExecutionException e) {
491             LOG.error("{} getTargetDpnForPacketOut: Failed to read data store for elan {}", LOGGING_PREFIX,
492                     elanInfo.getName());
493         }
494         return null;
495     }
496
497     public boolean isTunnel(String interfaceName) {
498         return interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName).augmentation(IfTunnel.class) != null;
499     }
500 }