2 * Copyright (c) 2016, 2018 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.vpnmanager;
10 import com.google.common.base.Optional;
11 import com.google.common.primitives.Ints;
12 import java.math.BigInteger;
13 import java.net.InetAddress;
14 import java.net.UnknownHostException;
15 import java.util.List;
16 import java.util.concurrent.ExecutionException;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
22 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
23 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
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.packet.Ethernet;
28 import org.opendaylight.genius.mdsalutil.packet.IPv4;
29 import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
30 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
31 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
32 import org.opendaylight.netvirt.vpnmanager.utilities.VpnManagerCounters;
33 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
34 import org.opendaylight.openflowplugin.libraries.liblldp.BufferException;
35 import org.opendaylight.openflowplugin.libraries.liblldp.HexEncode;
36 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
37 import org.opendaylight.openflowplugin.libraries.liblldp.Packet;
38 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
39 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
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.genius.interfacemanager.rev160406.IfTunnel;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.tag.name.map.ElanTagName;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.networkmaps.NetworkMap;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
63 public class SubnetRoutePacketInHandler implements PacketProcessingListener {
64 private static final Logger LOG = LoggerFactory.getLogger(SubnetRoutePacketInHandler.class);
65 private static final String LOGGING_PREFIX = "SUBNETROUTE:";
66 private final DataBroker dataBroker;
67 private final PacketProcessingService packetService;
68 private final OdlInterfaceRpcService odlInterfaceRpcService;
69 private final ICentralizedSwitchProvider centralizedSwitchProvider;
70 private final IInterfaceManager interfaceManager;
71 private final VpnManagerCounters vpnManagerCounters;
72 private final Ipv6NdUtilService ipv6NdUtilService;
73 private final VpnUtil vpnUtil;
76 public SubnetRoutePacketInHandler(final DataBroker dataBroker, final PacketProcessingService packetService,
77 final OdlInterfaceRpcService odlInterfaceRpcService,
78 final ICentralizedSwitchProvider centralizedSwitchProvider, final IInterfaceManager interfaceManager,
79 VpnManagerCounters vpnManagerCounters, final Ipv6NdUtilService ipv6NdUtilService, VpnUtil vpnUtil) {
80 this.dataBroker = dataBroker;
81 this.packetService = packetService;
82 this.odlInterfaceRpcService = odlInterfaceRpcService;
83 this.centralizedSwitchProvider = centralizedSwitchProvider;
84 this.interfaceManager = interfaceManager;
85 this.vpnManagerCounters = vpnManagerCounters;
86 this.ipv6NdUtilService = ipv6NdUtilService;
87 this.vpnUtil = vpnUtil;
91 public void onPacketReceived(PacketReceived notification) {
93 short tableId = notification.getTableId().getValue();
94 LOG.trace("{} onPacketReceived: Packet punted from table {}", LOGGING_PREFIX, tableId);
95 byte[] data = notification.getPayload();
96 if (notification.getMatch() == null || notification.getMatch().getMetadata() == null) {
97 LOG.error("{} onPacketReceived: Received from table {} where the match or metadata are null",
98 LOGGING_PREFIX, tableId);
101 BigInteger metadata = notification.getMatch().getMetadata().getMetadata();
102 Ethernet res = new Ethernet();
104 if (tableId == NwConstants.L3_SUBNET_ROUTE_TABLE) {
105 LOG.trace("{} onPacketReceived: Some packet received as {}", LOGGING_PREFIX, notification);
107 res.deserialize(data, 0, data.length * NetUtils.NUM_BITS_IN_A_BYTE);
108 } catch (PacketException e) {
109 LOG.error("{} onPacketReceived: Failed to decode Packet ", LOGGING_PREFIX, e);
110 vpnManagerCounters.subnetRoutePacketFailed();
113 byte[] srcIpBytes = null;
114 byte[] dstIpBytes = null;
117 String srcMac = NWUtil.toStringMacAddress(res.getSourceMACAddress());
119 Packet pkt = res.getPayload();
120 if (pkt instanceof IPv4) {
121 IPv4 ipv4 = (IPv4) pkt;
122 srcIpBytes = Ints.toByteArray(ipv4.getSourceAddress());
123 dstIpBytes = Ints.toByteArray(ipv4.getDestinationAddress());
124 // It is an ARP request on a configured VPN. So we must
125 // attempt to respond.
128 // TODO: IPv6 deserializer
129 int ethType = BitBufferHelper.getInt(
130 BitBufferHelper.getBits(data, VpnConstants.ETHTYPE_START, VpnConstants.TWO_BYTES));
131 if (ethType == VpnConstants.IP_V6_ETHTYPE) {
132 srcIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 64, 128);
133 dstIpBytes = BitBufferHelper.getBits(data, VpnConstants.IP_V6_HDR_START + 192, 128);
136 if (srcIpBytes == null || dstIpBytes == null) {
137 LOG.trace("{} onPacketReceived: Non-IP packet received as {}", LOGGING_PREFIX, notification);
140 srcIpStr = NWUtil.toStringIpAddress(srcIpBytes);
141 dstIpStr = NWUtil.toStringIpAddress(dstIpBytes);
143 handleIpPackets(srcIpBytes, dstIpBytes, srcIpStr, dstIpStr, srcMac, metadata);
145 } catch (UnknownHostException | InterruptedException | ExecutionException | BufferException ex) {
146 // Failed to handle packet
147 vpnManagerCounters.subnetRoutePacketFailed();
148 LOG.error("{} onPacketReceived: Failed to handle subnetroute packet.", LOGGING_PREFIX, ex);
149 } catch (ReadFailedException e) {
150 vpnManagerCounters.subnetRoutePacketFailed();
151 LOG.error("{} onPacketReceived: Failed to read data-store.", LOGGING_PREFIX, e);
155 // All Arp responses learning for invisble IPs is handled by
156 // ArpNotificationHandler
160 private void handleIpPackets(byte[] srcIp, byte[] dstIp, String srcIpStr, String dstIpStr, String srcMac,
162 throws UnknownHostException, InterruptedException, ExecutionException, ReadFailedException {
163 long vpnId = MetaDataUtil.getVpnIdFromMetadata(metadata);
165 LOG.info("{} onPacketReceived: Processing IP Packet received with Source IP {} and Target IP {}"
166 + " and vpnId {}", LOGGING_PREFIX, srcIpStr, dstIpStr, vpnId);
168 Optional<VpnIds> vpnIdsOptional = SingleTransactionDataBroker.syncReadOptional(dataBroker,
169 LogicalDatastoreType.CONFIGURATION, VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId));
171 if (!vpnIdsOptional.isPresent()) {
172 // Donot trigger subnetroute logic for packets from
174 vpnManagerCounters.subnetRoutePacketIgnored();
175 LOG.info("{} onPacketReceived: Ignoring IPv4 packet with destination Ip {} and source Ip {}"
176 + " as it came on unknown VPN with ID {}", LOGGING_PREFIX, dstIpStr, srcIpStr, vpnId);
180 String vpnIdVpnInstanceName = vpnIdsOptional.get().getVpnInstanceName();
181 if (vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnIdVpnInstanceName, dstIpStr) != null) {
182 vpnManagerCounters.subnetRoutePacketIgnored();
183 LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source IP {} vpnId {} "
184 + "is a valid Neutron port,ignoring subnet route processing", LOGGING_PREFIX, dstIpStr,
189 if (vpnUtil.getLearntVpnVipToPort(vpnIdVpnInstanceName, dstIpStr) != null) {
190 vpnManagerCounters.subnetRoutePacketIgnored();
191 LOG.info("{} onPacketReceived: IP Packet received with Target IP {} source Ip {} vpnId {}"
192 + " is an already discovered IPAddress, ignoring subnet route processing",
193 LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
197 long elanTag = MetaDataUtil.getElanTagFromMetadata(metadata);
199 vpnManagerCounters.subnetRoutePacketFailed();
200 LOG.error("{} onPacketReceived: elanTag value from metadata found to be 0, for IP "
201 + " Packet received with Target IP {} src Ip {} vpnId {}",
202 LOGGING_PREFIX, dstIpStr, srcIp, vpnId);
206 if (!vpnIdsOptional.get().isExternalVpn()) {
207 handleInternalVpnSubnetRoutePacket(metadata, dstIp, srcIpStr, dstIpStr, vpnIdVpnInstanceName,
212 handleBgpVpnSubnetRoute(srcMac, dstIp, dstIpStr, srcIpStr, elanTag);
215 private void handleBgpVpnSubnetRoute(String srcMac, byte[] dstIp, String dstIpStr, String srcIpStr,
216 long elanTag) throws UnknownHostException {
217 LOG.info("{} handleBgpVpnSubnetRoute: Processing IP Packet received with Source IP {} and Target IP {}"
218 + " and elan Tag {}", LOGGING_PREFIX, srcIpStr, dstIpStr, elanTag);
219 SubnetOpDataEntry targetSubnetForPacketOut =
220 getTargetSubnetForPacketOut(elanTag, dstIpStr);
221 if (targetSubnetForPacketOut != null) {
222 // Handle subnet routes ip requests
223 transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(), srcIpStr, srcMac, dstIp, dstIpStr, elanTag);
225 vpnManagerCounters.subnetRoutePacketFailed();
226 LOG.debug("{} handleBgpVpnSubnetRoute: Could not find target subnet for packet out {}", LOGGING_PREFIX,
231 private void handleInternalVpnSubnetRoutePacket(BigInteger metadata, byte[] dstIp, String srcIpStr, String dstIpStr,
232 String vpnIdVpnInstanceName, long elanTag)
233 throws InterruptedException, ExecutionException, UnknownHostException {
234 String vmVpnInterfaceName = vpnUtil.getVpnInterfaceName(metadata);
235 if (isTunnel(vmVpnInterfaceName)) {
236 handlePacketFromTunnelToExternalNetwork(vpnIdVpnInstanceName, srcIpStr, dstIp, dstIpStr, elanTag);
238 VpnInterface vmVpnInterface = vpnUtil.getVpnInterface(vmVpnInterfaceName);
239 if (vmVpnInterface == null) {
240 LOG.error("Vpn interface {} doesn't exist.", vmVpnInterfaceName);
241 vpnManagerCounters.subnetRoutePacketFailed();
244 if (VpnHelper.doesVpnInterfaceBelongToVpnInstance(vpnIdVpnInstanceName,
245 vmVpnInterface.getVpnInstanceNames())
246 && !vpnUtil.isBgpVpnInternet(vpnIdVpnInstanceName)) {
247 LOG.trace("Unknown IP is in internal network");
248 handlePacketToInternalNetwork(dstIp, dstIpStr, elanTag);
250 LOG.trace("Unknown IP is in external network");
251 String vpnName = vpnUtil.getInternetVpnFromVpnInstanceList(vmVpnInterface.getVpnInstanceNames());
252 if (vpnName != null) {
253 handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
255 vpnName = VpnHelper.getFirstVpnNameFromVpnInterface(vmVpnInterface);
256 LOG.trace("Unknown IP is in external network, but internet VPN not found." + " fallback to first VPN");
257 handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), vpnName, dstIp, dstIpStr, elanTag);
263 private void transmitArpOrNsPacket(BigInteger dpnId, String sourceIpAddress, String sourceMac, byte[] dstIpBytes,
264 String dstIpAddress, long elanTag) throws UnknownHostException {
265 long groupid = VpnUtil.getRemoteBCGroup(elanTag);
266 if (NWUtil.isIpv4Address(dstIpAddress)) {
267 LOG.debug("Sending ARP: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}", sourceIpAddress, sourceMac,
268 dstIpAddress, dpnId, elanTag);
269 vpnManagerCounters.subnetRoutePacketArpSent();
271 TransmitPacketInput packetInput =
272 ArpUtils.createArpRequestInput(dpnId, groupid, HexEncode.bytesFromHexString(sourceMac),
273 InetAddress.getByName(sourceIpAddress).getAddress(), dstIpBytes);
274 JdkFutures.addErrorLogging(packetService.transmitPacket(packetInput), LOG, "Transmit packet");
277 LOG.debug("Sending NS: srcIp={}, srcMac={}, dstIp={}, dpId={}, elan-tag={}", sourceIpAddress, sourceMac,
278 dstIpAddress, dpnId, elanTag);
279 vpnManagerCounters.subnetRoutePacketNsSent();
281 VpnUtil.sendNeighborSolicationToOfGroup(this.ipv6NdUtilService, new Ipv6Address(sourceIpAddress),
282 new MacAddress(sourceMac), new Ipv6Address(dstIpAddress), groupid, dpnId);
286 private void handlePacketToInternalNetwork(byte[] dstIp, String dstIpStr, long elanTag)
287 throws UnknownHostException {
289 SubnetOpDataEntry targetSubnetForPacketOut =
290 getTargetSubnetForPacketOut(elanTag, dstIpStr);
292 if (targetSubnetForPacketOut == null) {
293 LOG.debug("Couldn't find matching subnet for elan tag {} and destination ip {}", elanTag, dstIpStr);
294 vpnManagerCounters.subnetRoutePacketFailed();
298 Optional<Subnetmap> subnetMap = SingleTransactionDataBroker.syncReadOptional(dataBroker,
299 LogicalDatastoreType.CONFIGURATION,
300 VpnUtil.buildSubnetmapIdentifier(targetSubnetForPacketOut.getSubnetId()));
301 if (!subnetMap.isPresent()) {
302 LOG.debug("Couldn't find subnet map for subnet {}", targetSubnetForPacketOut.getSubnetId());
303 vpnManagerCounters.subnetRoutePacketFailed();
307 String sourceIp = subnetMap.get().getRouterInterfaceFixedIp();
308 if (sourceIp == null) {
309 LOG.debug("Subnet map {} doesn't have a router interface ip defined", subnetMap.get().getId());
310 vpnManagerCounters.subnetRoutePacketFailed();
314 String sourceMac = subnetMap.get().getRouterIntfMacAddress();
315 if (sourceMac == null) {
316 LOG.debug("Subnet map {} doesn't have a router interface mac address defined",
317 subnetMap.get().getId());
318 vpnManagerCounters.subnetRoutePacketFailed();
322 transmitArpOrNsPacket(targetSubnetForPacketOut.getNhDpnId(), sourceIp, sourceMac, dstIp, dstIpStr, elanTag);
323 } catch (ReadFailedException e) {
324 LOG.error("handlePacketToInternalNetwork: Failed to read data store for destIp {} elanTag {}", dstIpStr,
329 private void handlePacketFromTunnelToExternalNetwork(String vpnIdVpnInstanceName, String srcIpStr, byte[] dstIp,
330 String dstIpStr, long elanTag) throws UnknownHostException {
331 String routerId = vpnUtil.getAssociatedExternalRouter(srcIpStr);
332 if (null == routerId) {
333 LOG.debug("This ip is not associated with any external router: {}", srcIpStr);
335 handlePacketToExternalNetwork(new Uuid(vpnIdVpnInstanceName), routerId, dstIp, dstIpStr, elanTag);
338 private void handlePacketToExternalNetwork(Uuid vpnInstanceNameUuid, String routerId, byte[] dstIp, String dstIpStr,
339 long elanTag) throws UnknownHostException {
340 Routers externalRouter = vpnUtil.getExternalRouter(routerId);
341 if (externalRouter == null) {
342 vpnManagerCounters.subnetRoutePacketFailed();
343 LOG.debug("{} handlePacketToExternalNetwork: Can't find external router with id {}", LOGGING_PREFIX,
348 List<ExternalIps> externalIps = externalRouter.getExternalIps();
349 if (externalIps == null || externalIps.isEmpty()) {
350 vpnManagerCounters.subnetRoutePacketFailed();
351 LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have any external ips.",
352 LOGGING_PREFIX, externalRouter.getRouterName());
356 java.util.Optional<ExternalIps> externalIp = externalRouter.getExternalIps().stream()
357 .filter(eip -> vpnInstanceNameUuid.equals(eip.getSubnetId())).findFirst();
358 if (!externalIp.isPresent()) {
359 vpnManagerCounters.subnetRoutePacketFailed();
360 LOG.debug("{} handlePacketToExternalNetwork: Router {} doesn't have an external ip for subnet id {}.",
361 LOGGING_PREFIX, externalRouter.getRouterName(), vpnInstanceNameUuid);
365 BigInteger dpnId = centralizedSwitchProvider.getPrimarySwitchForRouter(externalRouter.getRouterName());
366 if (BigInteger.ZERO.equals(dpnId)) {
367 vpnManagerCounters.subnetRoutePacketFailed();
368 LOG.debug("{} handlePacketToExternalNetwork: Could not find primary switch for router {}.",
369 LOGGING_PREFIX, externalRouter.getRouterName());
373 transmitArpOrNsPacket(dpnId, externalIp.get().getIpAddress(), externalRouter.getExtGwMacAddress(), dstIp,
377 // return only the first VPN subnetopdataentry
378 private SubnetOpDataEntry getTargetSubnetForPacketOut(long elanTag, String ipAddress) {
379 ElanTagName elanInfo = vpnUtil.getElanInfoByElanTag(elanTag);
380 if (elanInfo == null) {
381 LOG.error("{} getTargetDpnForPacketOut: Unable to retrieve ElanInfo for elanTag {}", LOGGING_PREFIX,
386 Optional<NetworkMap> optionalNetworkMap = SingleTransactionDataBroker.syncReadOptional(dataBroker,
387 LogicalDatastoreType.CONFIGURATION, VpnUtil.buildNetworkMapIdentifier(new Uuid(
388 elanInfo.getName())));
389 if (!optionalNetworkMap.isPresent()) {
390 LOG.debug("{} getTargetDpnForPacketOut: No network map found for elan info {}", LOGGING_PREFIX,
394 List<Uuid> subnetList = optionalNetworkMap.get().getSubnetIdList();
395 LOG.debug("{} getTargetDpnForPacketOut: Obtained subnetList as {} for network {}", LOGGING_PREFIX,
396 subnetList, elanInfo.getName());
397 for (Uuid subnetId : subnetList) {
398 String vpnName = null;
399 Subnetmap sn = vpnUtil.getSubnetmapFromItsUuid(subnetId);
400 if (sn != null && sn.getVpnId() != null) {
401 vpnName = sn.getVpnId().getValue();
403 if (vpnName == null) {
406 Optional<SubnetOpDataEntry> optionalSubs;
407 optionalSubs = SingleTransactionDataBroker.syncReadOptional(dataBroker,
408 LogicalDatastoreType.OPERATIONAL, VpnUtil.buildSubnetOpDataEntryInstanceIdentifier(subnetId));
409 if (!optionalSubs.isPresent()) {
412 SubnetOpDataEntry subOpEntry = optionalSubs.get();
413 if (subOpEntry.getNhDpnId() != null) {
414 LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {}", LOGGING_PREFIX, subnetId.getValue());
415 IpPrefix cidr = new IpPrefix(subOpEntry.getSubnetCidr().toCharArray());
416 boolean match = NWUtil.isIpAddressInRange(new IpAddress(ipAddress.toCharArray()), cidr);
417 LOG.trace("{} getTargetDpnForPacketOut: Viewing Subnet {} matching {}", LOGGING_PREFIX,
418 subnetId.getValue(), match);
424 } catch (ReadFailedException e) {
425 LOG.error("{} getTargetDpnForPacketOut: Failed to read data store for elan {}", LOGGING_PREFIX,
431 public boolean isTunnel(String interfaceName) {
432 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang
433 .ietf.interfaces.rev140508.interfaces.Interface configIface =
434 interfaceManager.getInterfaceInfoFromConfigDataStore(interfaceName);
435 return configIface.augmentation(IfTunnel.class) != null;