2 * Copyright (c) 2016 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 org.opendaylight.controller.md.sal.binding.api.DataBroker;
12 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
13 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
14 import org.opendaylight.netvirt.elanmanager.api.IElanService;
15 import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
16 import org.opendaylight.genius.mdsalutil.MDSALUtil;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceived;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceived;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChanged;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.Floatingips;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.floatingips.attributes.floatingips.Floatingip;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.PortKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.Subnets;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.SubnetKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
42 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
43 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
44 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import com.google.common.util.concurrent.FutureCallback;
52 import com.google.common.util.concurrent.Futures;
53 import com.google.common.util.concurrent.JdkFutureAdapters;
54 import java.util.concurrent.Future;
55 import java.util.Arrays;
56 import java.util.List;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 import java.math.BigInteger;
62 public class ArpNotificationHandler implements OdlArputilListener {
63 private static final Logger LOG = LoggerFactory.getLogger(ArpNotificationHandler.class);
64 DataBroker dataBroker;
65 VpnInterfaceManager vpnIfManager;
66 IdManagerService idManager;
67 OdlArputilService arpManager;
68 final IElanService elanService;
69 ArpScheduler arpScheduler;
70 OdlInterfaceRpcService ifaceMgrRpcService;
72 public ArpNotificationHandler(DataBroker dataBroker, VpnInterfaceManager vpnIfMgr,
73 final IElanService elanService, IdManagerService idManager, OdlArputilService arpManager,
74 ArpScheduler arpScheduler, OdlInterfaceRpcService ifaceMgrRpcService) {
75 this.dataBroker = dataBroker;
76 vpnIfManager = vpnIfMgr;
77 this.elanService = elanService;
78 this.idManager = idManager;
79 this.arpManager = arpManager;
80 this.arpScheduler = arpScheduler;
81 this.ifaceMgrRpcService = ifaceMgrRpcService;
85 public void onMacChanged(MacChanged notification){
90 public void onArpRequestReceived(ArpRequestReceived notification){
91 LOG.trace("ArpNotification Request Received from interface {} and IP {} having MAC {} target destination {}",
92 notification.getInterface(), notification.getSrcIpaddress().getIpv4Address().getValue(),
93 notification.getSrcMac().getValue(),notification.getDstIpaddress().getIpv4Address().getValue());
94 String srcInterface = notification.getInterface();
95 IpAddress srcIP = notification.getSrcIpaddress();
96 PhysAddress srcMac = notification.getSrcMac();
97 IpAddress targetIP = notification.getDstIpaddress();
98 BigInteger metadata = notification.getMetadata();
99 if (metadata != null && metadata != BigInteger.ZERO) {
100 long vpnId = MetaDataUtil.getVpnIdFromMetadata(metadata);
101 // Respond to ARP request only if vpnservice is configured on the interface
102 if (VpnUtil.isVpnInterfaceConfigured(dataBroker, srcInterface)) {
103 LOG.info("Received ARP Request for interface {} ", srcInterface);
104 InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds>
105 vpnIdsInstanceIdentifier = VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId);
106 Optional<VpnIds> vpnIdsOptional
107 = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdsInstanceIdentifier);
108 if (!vpnIdsOptional.isPresent()) {
109 // Donot respond to ARP requests on unknown VPNs
110 LOG.trace("ARP NO_RESOLVE: VPN {} not configured. Ignoring responding to ARP requests on this VPN", vpnId);
113 String vpnName = vpnIdsOptional.get().getVpnInstanceName();
114 String ipToQuery = notification.getSrcIpaddress().getIpv4Address().getValue();
115 LOG.trace("ArpRequest being processed for Source IP {}", ipToQuery);
116 VpnIds vpnIds = vpnIdsOptional.get();
117 VpnPortipToPort vpnPortipToPort = VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnIds.getVpnInstanceName(), ipToQuery);
118 if (vpnPortipToPort != null) {
119 String oldPortName = vpnPortipToPort.getPortName();
120 String oldMac = vpnPortipToPort.getMacAddress();
121 if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
122 //MAC has changed for requested IP
123 LOG.trace("ARP request Source IP/MAC data etmodified for IP {} with MAC {} and Port {}",
124 ipToQuery, srcMac, srcInterface);
125 if (!vpnPortipToPort.isConfig()) {
126 synchronized ((vpnName + ipToQuery).intern()) {
127 removeMipAdjacency(vpnName, oldPortName, srcIP);
128 VpnUtil.removeVpnPortFixedIpToPort(dataBroker, vpnName, ipToQuery);
132 } catch (Exception e) {
135 //MAC mismatch for a Neutron learned IP
136 LOG.warn("MAC Address mismatach for Interface {} having a Mac {}, IP {} and Arp learnt Mac {}",
137 oldPortName, oldMac, ipToQuery, srcMac.getValue());
141 arpScheduler.refreshArpEntry(vpnPortipToPort);
144 synchronized ((vpnName + ipToQuery).intern()) {
145 VpnUtil.createVpnPortFixedIpToPort(dataBroker, vpnName, ipToQuery, srcInterface, srcMac.getValue(), false, false, true);
146 addMipAdjacency(vpnName, srcInterface, srcIP, null);
149 String targetIpToQuery = notification.getDstIpaddress().getIpv4Address().getValue();
150 VpnPortipToPort vpnTargetIpToPort = VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker,
151 vpnIds.getVpnInstanceName(), targetIpToQuery);
153 if (vpnIds.isExternalVpn()) {
154 handleArpRequestForExternalVpn(srcInterface, srcIP, srcMac, targetIP, targetIpToQuery,
158 if (vpnTargetIpToPort != null && vpnTargetIpToPort.isSubnetIp()) { // handle router interfaces ARP
159 handleArpRequestForSubnetIp(srcInterface, srcIP, srcMac, targetIP, vpnTargetIpToPort);
162 if (elanService.isExternalInterface(srcInterface)) {
163 handleArpRequestFromExternalInterface(srcInterface, srcIP, srcMac, targetIP);
170 private void handleArpRequestForSubnetIp(String srcInterface, IpAddress srcIP, PhysAddress srcMac,
171 IpAddress targetIP, VpnPortipToPort vpnTargetIpToPort) {
172 String macAddress = vpnTargetIpToPort.getMacAddress();
173 PhysAddress targetMac = new PhysAddress(macAddress);
174 processArpRequest(srcIP, srcMac, targetIP, targetMac, srcInterface);
178 private void handleArpRequestForExternalVpn(String srcInterface, IpAddress srcIP, PhysAddress srcMac,
179 IpAddress targetIP, String targetIpToQuery, VpnPortipToPort vpnTargetIpToPort) {
180 if (vpnTargetIpToPort != null) {
181 if (vpnTargetIpToPort.isSubnetIp()) {
182 handleArpRequestForSubnetIp(srcInterface, srcIP, srcMac, targetIP, vpnTargetIpToPort);
186 // Respond for gateway Ips ARP requests if L3vpn configured without a router
189 Uuid portUuid = new Uuid(srcInterface);
190 InstanceIdentifier<Port> inst = InstanceIdentifier.create(Neutron.class).child(Ports.class)
191 .child(Port.class, new PortKey(portUuid));
192 Optional<Port> port = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, inst);
193 if (port.isPresent()) {
195 Uuid subnetUUID = prt.getFixedIps().get(0).getSubnetId();
196 LOG.trace("Subnet UUID for this VPN Interface is {}", subnetUUID);
197 SubnetKey subnetkey = new SubnetKey(subnetUUID);
198 InstanceIdentifier<Subnet> subnetidentifier = InstanceIdentifier.create(Neutron.class)
199 .child(Subnets.class).child(Subnet.class, subnetkey);
200 Optional<Subnet> subnet = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
202 if (subnet.isPresent()) {
203 gw = subnet.get().getGatewayIp().getIpv4Address().getValue();
204 if (targetIpToQuery.equalsIgnoreCase(gw)) {
205 LOG.trace("Target Destination matches the Gateway IP {} so respond for ARP", gw);
206 processArpRequest(srcIP, srcMac, targetIP, null, srcInterface);
213 public void onArpResponseReceived(ArpResponseReceived notification){
214 LOG.trace("ArpNotification Response Received from interface {} and IP {} having MAC {}",notification.getInterface(),
215 notification.getIpaddress().getIpv4Address().getValue(), notification.getMacaddress().getValue());
216 String srcInterface = notification.getInterface();
217 IpAddress srcIP = notification.getIpaddress();
218 PhysAddress srcMac = notification.getMacaddress();
219 BigInteger metadata = notification.getMetadata();
220 if (metadata != null && metadata != BigInteger.ZERO) {
221 long vpnId = MetaDataUtil.getVpnIdFromMetadata(metadata);
222 InstanceIdentifier<VpnIds>
223 vpnIdsInstanceIdentifier = VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId);
224 Optional<VpnIds> vpnIdsOptional
225 = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdsInstanceIdentifier);
226 if (!vpnIdsOptional.isPresent()) {
227 // Donot respond to ARP requests on unknown VPNs
228 LOG.trace("ARP NO_RESOLVE: VPN {} not configured. Ignoring responding to ARP requests on this VPN", vpnId);
231 if (VpnUtil.isVpnInterfaceConfigured(dataBroker, srcInterface)) {
232 String vpnName = vpnIdsOptional.get().getVpnInstanceName();
233 String ipToQuery = notification.getIpaddress().getIpv4Address().getValue();
234 VpnIds vpnIds = vpnIdsOptional.get();
235 VpnPortipToPort vpnPortipToPort = VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnIds
236 .getVpnInstanceName(), ipToQuery);
237 if (vpnPortipToPort != null) {
238 String oldMac = vpnPortipToPort.getMacAddress();
239 String oldPortName = vpnPortipToPort.getPortName();
240 if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
241 //MAC has changed for requested IP
242 LOG.trace("ARP response Source IP/MAC data modified for IP {} with MAC {} and Port {}",
243 ipToQuery, srcMac, srcInterface);
244 if (!vpnPortipToPort.isConfig()) {
245 synchronized ((vpnName + ipToQuery).intern()) {
246 removeMipAdjacency(vpnName, oldPortName, srcIP);
247 VpnUtil.removeVpnPortFixedIpToPort(dataBroker, vpnName, ipToQuery);
251 } catch (Exception e) {
254 //MAC mismatch for a Neutron learned IP set learnt back to false
255 LOG.warn("MAC Address mismatch for Interface {} having a Mac {} , IP {} and Arp learnt Mac {}",
256 srcInterface, oldMac, ipToQuery, srcMac.getValue());
259 arpScheduler.refreshArpEntry(vpnPortipToPort);
262 synchronized ((vpnName + ipToQuery).intern()) {
263 VpnUtil.createVpnPortFixedIpToPort(dataBroker, vpnName, ipToQuery, srcInterface, srcMac.getValue(), false, false, true);
264 addMipAdjacency(vpnName, srcInterface, srcIP, srcMac.getValue());
271 private void handleArpRequestFromExternalInterface(String srcInterface, IpAddress srcIP, PhysAddress srcMac,
272 IpAddress targetIP) {
273 Port port = VpnUtil.getNeutronPortForFloatingIp(dataBroker, targetIP);
274 String floatingIp = targetIP.getIpv4Address().getValue();
276 LOG.trace("No neutron port found for with floating ip {}", floatingIp);
280 MacAddress targetMac = port.getMacAddress();
281 if (targetMac == null) {
282 LOG.trace("No mac address found for floating ip {}", floatingIp);
286 // don't allow ARP responses if it arrives from different dpn
287 String localPortInterface = getFloatingInternalInterface(floatingIp);
288 if (localPortInterface != null && !localPortInterface.isEmpty()) {
289 BigInteger dpnIdSrc = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, srcInterface);
290 BigInteger dpnIdLocal = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, localPortInterface);
291 if (!dpnIdSrc.equals(dpnIdLocal)) {
292 LOG.trace("Not same dpnId, so don't respond for ARP - dpnIdSrc:{} dpnIdLocal:{}", dpnIdSrc, dpnIdLocal);
296 LOG.trace("Target destination matches floating IP {} so respond for ARP", floatingIp);
297 vpnIfManager.processArpRequest(srcIP, srcMac, targetIP, new PhysAddress(targetMac.getValue()), srcInterface);
300 public void processArpRequest(IpAddress srcIP, PhysAddress srcMac, IpAddress targetIP, PhysAddress targetMac,
301 String srcInterface){
302 //Build ARP response with ARP requests TargetIp TargetMac as the Arp Response SrcIp and SrcMac
303 SendArpResponseInput input = new SendArpResponseInputBuilder().setInterface(srcInterface)
304 .setDstIpaddress(srcIP).setDstMacaddress(srcMac).setSrcIpaddress(targetIP).setSrcMacaddress(targetMac).build();
305 final String msgFormat = String.format("Send ARP Response on interface %s to destination %s", srcInterface, srcIP);
306 Future<RpcResult<Void>> future = arpManager.sendArpResponse(input);
307 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(future), new FutureCallback<RpcResult<Void>>() {
309 public void onFailure(Throwable error) {
310 LOG.error("Error - {}", msgFormat, error);
314 public void onSuccess(RpcResult<Void> result) {
315 if(!result.isSuccessful()) {
316 LOG.warn("Rpc call to {} failed", msgFormat);
318 LOG.debug("Successful RPC Result - {}", msgFormat);
324 private void addMipAdjacency(String vpnName, String vpnInterface, IpAddress prefix, String mipMacAddress){
326 LOG.trace("Adding {} adjacency to VPN Interface {} ",prefix,vpnInterface);
327 InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
328 InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
329 synchronized (vpnInterface.intern()) {
330 Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
331 String nextHopIpAddr = null;
332 String nextHopMacAddress = null;
333 String ip = prefix.getIpv4Address().getValue();
334 if (adjacencies.isPresent()) {
335 List<Adjacency> adjacencyList = adjacencies.get().getAdjacency();
336 ip = VpnUtil.getIpPrefix(ip);
337 for (Adjacency adjacs : adjacencyList) {
338 if (adjacs.isPrimaryAdjacency()) {
339 nextHopIpAddr = adjacs.getIpAddress();
340 nextHopMacAddress = adjacs.getMacAddress();
344 if (nextHopIpAddr != null) {
345 String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
347 VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
348 VpnUtil.getNextHopLabelKey((rd != null) ? rd : vpnName, ip));
350 LOG.error("Unable to fetch label from Id Manager. Bailing out of adding MIP adjacency {} "
351 + "to vpn interface {} for vpn {}", ip, vpnInterface, vpnName);
354 String nextHopIp = nextHopIpAddr.split("/")[0];
355 AdjacencyBuilder newAdjBuilder = new AdjacencyBuilder().setIpAddress(ip).setKey
356 (new AdjacencyKey(ip)).setNextHopIpList(Arrays.asList(nextHopIp));
357 if (mipMacAddress != null) {
358 newAdjBuilder.setMacAddress(mipMacAddress);
360 adjacencyList.add(newAdjBuilder.build());
361 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
362 VpnInterface newVpnIntf = new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(vpnInterface)).
363 setName(vpnInterface).setVpnInstanceName(vpnName).addAugmentation(Adjacencies.class, aug)
365 VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId, newVpnIntf);
366 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
373 private void removeMipAdjacency(String vpnName, String vpnInterface, IpAddress prefix) {
374 String ip = VpnUtil.getIpPrefix(prefix.getIpv4Address().getValue());
375 LOG.trace("Removing {} adjacency from Old VPN Interface {} ", ip,vpnInterface);
376 InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
377 InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
378 synchronized (vpnInterface.intern()) {
379 Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
380 if (adjacencies.isPresent()) {
381 InstanceIdentifier<Adjacency> adjacencyIdentifier = InstanceIdentifier.builder(VpnInterfaces.class).
382 child(VpnInterface.class, new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class)
383 .child(Adjacency.class, new AdjacencyKey(ip)).build();
384 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
385 LOG.trace("Successfully Deleted Adjacency into VpnInterface {}", vpnInterface);
390 public String getFloatingInternalInterface(String targetIpValue) {
391 if (targetIpValue == null || targetIpValue.isEmpty()) {
394 InstanceIdentifier<Floatingips> identifier = InstanceIdentifier.create(Neutron.class).child(Floatingips.class);
395 Optional<Floatingips> optInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, identifier);
396 if (optInterface.isPresent()) {
397 Floatingips fips = optInterface.get();
399 for (Floatingip fip : fips.getFloatingip()) {
400 String ipv4Addr = fip.getFloatingIpAddress().getIpv4Address().getValue();
401 if (targetIpValue.equals(ipv4Addr)) {
402 return fip.getPortId().getValue();