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 com.google.common.cache.Cache;
12 import com.google.common.cache.CacheBuilder;
13 import java.math.BigInteger;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.concurrent.TimeUnit;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.apache.commons.lang3.tuple.ImmutablePair;
23 import org.apache.commons.lang3.tuple.Pair;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.genius.mdsalutil.NWUtil;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
31 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
32 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceived;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceived;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChanged;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInterfaceOpData;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.AdjacencyType;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntryKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 public class ArpNotificationHandler implements OdlArputilListener {
59 private static final Logger LOG = LoggerFactory.getLogger(ArpNotificationHandler.class);
60 // temp where Key is VPNInstance+IP and value is timestamp
61 private final Cache<Pair<String, String>, BigInteger> migrateArpCache;
63 private final DataBroker dataBroker;
64 private final IdManagerService idManager;
65 private final IInterfaceManager interfaceManager;
66 private final VpnConfig config;
69 public ArpNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
70 IInterfaceManager interfaceManager, VpnConfig vpnConfig) {
71 this.dataBroker = dataBroker;
72 this.idManager = idManager;
73 this.interfaceManager = interfaceManager;
74 this.config = vpnConfig;
76 long duration = config.getArpLearnTimeout() * 10;
77 long cacheSize = config.getArpCacheSize().longValue();
79 CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterWrite(duration,
80 TimeUnit.MILLISECONDS).build();
84 public void onMacChanged(MacChanged notification) {
89 public void onArpRequestReceived(ArpRequestReceived notification) {
90 String srcInterface = notification.getInterface();
91 IpAddress srcIP = notification.getSrcIpaddress();
92 PhysAddress srcMac = notification.getSrcMac();
93 IpAddress targetIP = notification.getDstIpaddress();
94 BigInteger metadata = notification.getMetadata();
95 boolean isGarp = srcIP.equals(targetIP);
97 LOG.info("ArpNotification Non-Gratuitous Request Received from "
98 + "interface {} and IP {} having MAC {} target destination {}, ignoring..",
99 srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
100 targetIP.getIpv4Address().getValue());
103 LOG.info("ArpNotification Gratuitous Request Received from "
104 + "interface {} and IP {} having MAC {} target destination {}, learning MAC",
105 srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
106 targetIP.getIpv4Address().getValue());
107 processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
111 public void onArpResponseReceived(ArpResponseReceived notification) {
112 String srcInterface = notification.getInterface();
113 IpAddress srcIP = notification.getSrcIpaddress();
114 PhysAddress srcMac = notification.getSrcMac();
115 LOG.info("ArpNotification Response Received from interface {} and IP {} having MAC {}, learning MAC",
116 srcInterface, srcIP.getIpv4Address().getValue(), srcMac.getValue());
117 List<Adjacency> adjacencies = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker, srcInterface);
118 if (adjacencies != null) {
119 for (Adjacency adj : adjacencies) {
120 String ipAddress = adj.getIpAddress();
122 if (NWUtil.isIpInSubnet(NWUtil.ipAddressToInt(srcIP.getIpv4Address().getValue()), ipAddress)) {
125 } catch (UnknownHostException e) {
126 LOG.error("Subnet string {} not convertible to InetAdddress", srcIP, e);
130 BigInteger metadata = notification.getMetadata();
131 IpAddress targetIP = notification.getDstIpaddress();
132 LOG.trace("ArpNotification Response Received from interface {} and IP {} having MAC {}, learning MAC",
133 srcInterface, srcIP.getIpv4Address().getValue(), srcMac.getValue());
134 processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
137 private void processArpLearning(String srcInterface, IpAddress srcIP, PhysAddress srcMac, BigInteger metadata,
139 if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
140 Optional<List<String>> vpnList = VpnUtil
141 .getVpnHandlingIpv4AssociatedWithInterface(dataBroker, srcInterface);
142 if (vpnList.isPresent()) {
143 for (String vpnName : vpnList.get()) {
144 LOG.info("Received ARP for sender MAC {} and sender IP {} via interface {}",
145 srcMac.getValue(), srcIP.getIpv4Address().getValue(), srcInterface);
146 String ipToQuery = srcIP.getIpv4Address().getValue();
147 LOG.info("ARP being processed for Source IP {}", ipToQuery);
148 VpnPortipToPort vpnPortipToPort =
149 VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ipToQuery);
150 if (vpnPortipToPort != null) {
151 /* This is a well known neutron port and so should be ignored
152 * from being discovered
156 LearntVpnVipToPort learntVpnVipToPort = VpnUtil.getLearntVpnVipToPort(dataBroker,
158 if (learntVpnVipToPort != null) {
159 String oldPortName = learntVpnVipToPort.getPortName();
160 String oldMac = learntVpnVipToPort.getMacAddress();
161 if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
162 //MAC has changed for requested IP
163 LOG.info("ARP Source IP/MAC data modified for IP {} with MAC {} and Port {}",
164 ipToQuery, srcMac, srcInterface);
165 synchronized ((vpnName + ipToQuery).intern()) {
166 removeMipAdjacency(vpnName, oldPortName, srcIP);
167 VpnUtil.removeLearntVpnVipToPort(dataBroker, vpnName, ipToQuery);
168 putVpnIpToMigrateArpCache(vpnName, ipToQuery, srcMac);
171 } else if (!isIpInArpMigrateCache(vpnName, ipToQuery)) {
172 learnMacFromArpPackets(vpnName, srcInterface, srcIP, srcMac, dstIP);
176 LOG.info("ARP NO_RESOLVE: VPN not configured. Ignoring responding to ARP requests from this"
177 + " Interface {}.", srcInterface);
184 private void learnMacFromArpPackets(String vpnName, String srcInterface,
185 IpAddress srcIP, PhysAddress srcMac, IpAddress dstIP) {
186 String ipToQuery = srcIP.getIpv4Address().getValue();
187 synchronized ((vpnName + ipToQuery).intern()) {
188 VpnUtil.createLearntVpnVipToPort(dataBroker, vpnName, ipToQuery, srcInterface, srcMac.getValue());
189 addMipAdjacency(vpnName, srcInterface, srcIP, srcMac.getValue(), dstIP);
193 private void addMipAdjacency(String vpnName, String vpnInterface, IpAddress srcPrefix, String mipMacAddress,
194 IpAddress dstPrefix) {
195 LOG.trace("Adding {} adjacency to VPN Interface {} ",srcPrefix,vpnInterface);
196 InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
197 InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
198 synchronized (vpnInterface.intern()) {
199 Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
200 String nextHopIpAddr = null;
201 String nextHopMacAddress = null;
202 String ip = srcPrefix.getIpv4Address().getValue();
203 if (interfaceManager.isExternalInterface(vpnInterface)) {
204 String subnetId = getSubnetId(vpnName, dstPrefix.getIpv4Address().getValue());
205 if (subnetId == null) {
206 LOG.trace("Can't find corresponding subnet for src IP {}, src MAC {}, dst IP {}, in VPN {}",
207 srcPrefix, mipMacAddress, dstPrefix, vpnName);
210 ip = VpnUtil.getIpPrefix(ip);
211 AdjacencyBuilder newAdjBuilder = new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip))
212 .setAdjacencyType(AdjacencyType.PrimaryAdjacency).setMacAddress(mipMacAddress)
213 .setSubnetId(new Uuid(subnetId)).setPhysNetworkFunc(true);
215 List<Adjacency> adjacencyList = adjacencies.isPresent()
216 ? adjacencies.get().getAdjacency() : new ArrayList<>();
218 adjacencyList.add(newAdjBuilder.build());
220 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
221 Optional<VpnInterface> optionalVpnInterface =
222 VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId);
223 VpnInterface newVpnIntf;
224 if (optionalVpnInterface.isPresent()) {
226 new VpnInterfaceBuilder(optionalVpnInterface.get())
227 .addAugmentation(Adjacencies.class, aug)
229 VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId, newVpnIntf);
231 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
235 if (adjacencies.isPresent()) {
236 List<Adjacency> adjacencyList = adjacencies.get().getAdjacency();
237 ip = VpnUtil.getIpPrefix(ip);
238 for (Adjacency adjacs : adjacencyList) {
239 if (adjacs.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
240 if (adjacs.getIpAddress().equals(ip)) {
241 LOG.error("The MIP {} is already present as a primary adjacency for interface {} vpn {}."
242 + "Skipping adjacency addition.", ip, vpnInterface, vpnName);
245 nextHopIpAddr = adjacs.getIpAddress();
246 nextHopMacAddress = adjacs.getMacAddress();
250 if (nextHopIpAddr != null) {
251 String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
253 VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
254 VpnUtil.getNextHopLabelKey(rd != null ? rd : vpnName, ip));
256 LOG.error("Unable to fetch label from Id Manager. Bailing out of adding MIP adjacency {} "
257 + "to vpn interface {} for vpn {}", ip, vpnInterface, vpnName);
260 String nextHopIp = nextHopIpAddr.split("/")[0];
261 AdjacencyBuilder newAdjBuilder =
262 new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip)).setNextHopIpList(
263 Collections.singletonList(nextHopIp)).setAdjacencyType(AdjacencyType.LearntIp);
264 if (mipMacAddress != null && !mipMacAddress.equalsIgnoreCase(nextHopMacAddress)) {
265 newAdjBuilder.setMacAddress(mipMacAddress);
267 adjacencyList.add(newAdjBuilder.build());
268 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
269 Optional<VpnInterface> optionalVpnInterface =
270 VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId);
271 VpnInterface newVpnIntf;
272 if (optionalVpnInterface.isPresent()) {
274 new VpnInterfaceBuilder(optionalVpnInterface.get())
275 .addAugmentation(Adjacencies.class, aug).build();
276 VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
277 vpnIfId, newVpnIntf);
279 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
286 private String getSubnetId(String vpnName, String ip) {
287 // Check if this IP belongs to a router_interface
288 VpnPortipToPort vpnPortipToPort =
289 VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ip);
290 if (vpnPortipToPort != null && vpnPortipToPort.isSubnetIp()) {
291 List<Adjacency> adjacecnyList = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker,
292 vpnPortipToPort.getPortName());
293 for (Adjacency adjacency : adjacecnyList) {
294 if (adjacency.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
295 return adjacency.getSubnetId().getValue();
300 // Check if this IP belongs to a router_gateway
301 List<Uuid> routerIds = VpnUtil.getExternalNetworkRouterIds(dataBroker, new Uuid(vpnName));
302 for (Uuid routerId : routerIds) {
303 Uuid subnetId = VpnUtil.getSubnetFromExternalRouterByIp(dataBroker, routerId, ip);
304 if (subnetId != null) {
305 return subnetId.getValue();
312 private void removeMipAdjacency(String vpnName, String vpnInterface, IpAddress prefix) {
313 String ip = VpnUtil.getIpPrefix(prefix.getIpv4Address().getValue());
314 LOG.trace("Removing {} adjacency from Old VPN Interface {} ", ip, vpnInterface);
315 InstanceIdentifier<VpnInterfaceOpDataEntry> vpnIfId = VpnUtil.getVpnInterfaceOpDataEntryIdentifier(
316 vpnInterface, vpnName);
317 InstanceIdentifier<AdjacenciesOp> path = vpnIfId.augmentation(AdjacenciesOp.class);
318 synchronized (vpnInterface.intern()) {
319 Optional<AdjacenciesOp> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
320 if (adjacencies.isPresent()) {
321 InstanceIdentifier<Adjacency> adjacencyIdentifierOp =
322 InstanceIdentifier.builder(VpnInterfaceOpData.class).child(VpnInterfaceOpDataEntry.class,
323 new VpnInterfaceOpDataEntryKey(vpnInterface, vpnName)).augmentation(AdjacenciesOp.class)
324 .child(Adjacency.class, new AdjacencyKey(ip)).build();
325 Optional<Adjacency> adjacencyOper = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
326 adjacencyIdentifierOp);
327 InstanceIdentifier<Adjacency> adjacencyIdentifierConf =
328 InstanceIdentifier.builder(VpnInterfaces.class).child(VpnInterface.class,
329 new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class).child(Adjacency.class,
330 new AdjacencyKey(ip)).build();
331 if (adjacencyOper.isPresent()) {
332 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifierConf);
333 LOG.info("Successfully deleted in configDS the learned-ip-adjacency for prefix {} on vpn {} for "
334 + "interface {} for adjacency {}", ip, vpnName, vpnInterface, adjacencyOper);
340 private void putVpnIpToMigrateArpCache(String vpnName, String ipToQuery, PhysAddress srcMac) {
341 long cacheSize = config.getArpCacheSize().longValue();
342 if (migrateArpCache.size() >= cacheSize) {
343 LOG.debug("ARP_MIGRATE_CACHE: max size {} reached, assuming cache eviction we still put IP {}"
344 + " vpnName {} with MAC {}", cacheSize, ipToQuery, vpnName, srcMac);
346 LOG.debug("ARP_MIGRATE_CACHE: add to dirty cache IP {} vpnName {} with MAC {}", ipToQuery, vpnName, srcMac);
347 migrateArpCache.put(new ImmutablePair<>(vpnName, ipToQuery),
348 new BigInteger(String.valueOf(System.currentTimeMillis())));
351 private boolean isIpInArpMigrateCache(String vpnName, String ipToQuery) {
352 if (migrateArpCache == null || migrateArpCache.size() == 0) {
355 Pair<String, String> keyPair = new ImmutablePair<>(vpnName, ipToQuery);
356 BigInteger prevTimeStampCached = migrateArpCache.getIfPresent(keyPair);
357 if (prevTimeStampCached == null) {
358 LOG.debug("ARP_MIGRATE_CACHE: there is no IP {} vpnName {} in dirty cache, so learn it",
362 if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getArpLearnTimeout()) {
363 LOG.debug("ARP_MIGRATE_CACHE: older than timeout value - remove from dirty cache IP {} vpnName {}",
365 migrateArpCache.invalidate(keyPair);
368 LOG.debug("ARP_MIGRATE_CACHE: younger than timeout value - ignore learning IP {} vpnName {}",