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.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.concurrent.TimeUnit;
20 import org.apache.commons.lang3.tuple.ImmutablePair;
21 import org.apache.commons.lang3.tuple.Pair;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
27 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
28 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceived;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceived;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChanged;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public class ArpNotificationHandler implements OdlArputilListener {
52 private static final Logger LOG = LoggerFactory.getLogger(ArpNotificationHandler.class);
53 // temp where Key is VPNInstance+IP and value is timestamp
54 private final Cache<Pair<String, String>, BigInteger> migrateArpCache;
56 DataBroker dataBroker;
57 IdManagerService idManager;
58 IInterfaceManager interfaceManager;
59 private final VpnConfig config;
62 public ArpNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
63 IInterfaceManager interfaceManager, VpnConfig vpnConfig) {
64 this.dataBroker = dataBroker;
65 this.idManager = idManager;
66 this.interfaceManager = interfaceManager;
67 this.config = vpnConfig;
69 long duration = config.getArpLearnTimeout() * 10;
70 long cacheSize = config.getArpCacheSize().longValue();
72 CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterWrite(duration,
73 TimeUnit.MILLISECONDS).build();
77 public void onMacChanged(MacChanged notification) {
82 public void onArpRequestReceived(ArpRequestReceived notification) {
83 String srcInterface = notification.getInterface();
84 IpAddress srcIP = notification.getSrcIpaddress();
85 PhysAddress srcMac = notification.getSrcMac();
86 IpAddress targetIP = notification.getDstIpaddress();
87 BigInteger metadata = notification.getMetadata();
88 boolean isGarp = srcIP.equals(targetIP);
90 LOG.trace("ArpNotification Non-Gratuitous Request Received from "
91 + "interface {} and IP {} having MAC {} target destination {}, ignoring..",
92 srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
93 targetIP.getIpv4Address().getValue());
96 LOG.trace("ArpNotification Gratuitous Request Received from "
97 + "interface {} and IP {} having MAC {} target destination {}, learning MAC",
98 srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
99 targetIP.getIpv4Address().getValue());
100 processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
104 public void onArpResponseReceived(ArpResponseReceived notification) {
105 String srcInterface = notification.getInterface();
106 IpAddress srcIP = notification.getSrcIpaddress();
107 PhysAddress srcMac = notification.getSrcMac();
108 BigInteger metadata = notification.getMetadata();
109 IpAddress targetIP = notification.getDstIpaddress();
110 LOG.trace("ArpNotification Response Received from interface {} and IP {} having MAC {}, learning MAC",
111 srcInterface, srcIP.getIpv4Address().getValue(), srcMac.getValue());
112 processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
115 private void processArpLearning(String srcInterface, IpAddress srcIP, PhysAddress srcMac, BigInteger metadata,
117 if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
118 long vpnId = MetaDataUtil.getVpnIdFromMetadata(metadata);
119 // Process ARP only if vpnservice is configured on the interface
120 InstanceIdentifier<VpnIds> vpnIdsInstanceIdentifier = VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId);
121 Optional<VpnIds> vpnIdsOptional
122 = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdsInstanceIdentifier);
123 if (!vpnIdsOptional.isPresent()) {
124 LOG.trace("ARP NO_RESOLVE: VPN {} not configured. Ignoring responding to ARP requests on this VPN",
128 VpnIds vpnIds = vpnIdsOptional.get();
129 String vpnName = vpnIds.getVpnInstanceName();
130 if (VpnUtil.isInterfaceAssociatedWithVpn(dataBroker, vpnName, srcInterface)) {
131 LOG.debug("Received ARP for sender MAC {} and sender IP {} via interface {}",
132 srcMac.getValue(), srcIP.getIpv4Address().getValue(), srcInterface);
133 String ipToQuery = srcIP.getIpv4Address().getValue();
134 LOG.trace("ARP being processed for Source IP {}", ipToQuery);
135 VpnPortipToPort vpnPortipToPort =
136 VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ipToQuery);
137 if (vpnPortipToPort != null) {
138 /* This is a well known neutron port and so should be ignored
139 * from being discovered
143 LearntVpnVipToPort learntVpnVipToPort = VpnUtil.getLearntVpnVipToPort(dataBroker, vpnName, ipToQuery);
144 if (learntVpnVipToPort != null) {
145 String oldPortName = learntVpnVipToPort.getPortName();
146 String oldMac = learntVpnVipToPort.getMacAddress();
147 if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
148 //MAC has changed for requested IP
149 LOG.trace("ARP Source IP/MAC data modified for IP {} with MAC {} and Port {}",
150 ipToQuery, srcMac, srcInterface);
151 synchronized ((vpnName + ipToQuery).intern()) {
152 removeMipAdjacency(vpnName, oldPortName, srcIP);
153 VpnUtil.removeLearntVpnVipToPort(dataBroker, vpnName, ipToQuery);
154 putVpnIpToMigrateArpCache(vpnName, ipToQuery, srcMac);
157 } else if (!isIpInArpMigrateCache(vpnName, ipToQuery)) {
158 learnMacFromArpPackets(vpnName, srcInterface, srcIP, srcMac, dstIP);
164 private void learnMacFromArpPackets(String vpnName, String srcInterface,
165 IpAddress srcIP, PhysAddress srcMac, IpAddress dstIP) {
166 String ipToQuery = srcIP.getIpv4Address().getValue();
167 synchronized ((vpnName + ipToQuery).intern()) {
168 VpnUtil.createLearntVpnVipToPort(dataBroker, vpnName, ipToQuery, srcInterface, srcMac.getValue());
169 addMipAdjacency(vpnName, srcInterface, srcIP, srcMac.getValue(), dstIP);
173 private void addMipAdjacency(String vpnName, String vpnInterface, IpAddress srcPrefix, String mipMacAddress,
174 IpAddress dstPrefix) {
175 LOG.trace("Adding {} adjacency to VPN Interface {} ",srcPrefix,vpnInterface);
176 InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
177 InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
178 synchronized (vpnInterface.intern()) {
179 Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
180 String nextHopIpAddr = null;
181 String nextHopMacAddress = null;
182 String ip = srcPrefix.getIpv4Address().getValue();
183 if (interfaceManager.isExternalInterface(vpnInterface)) {
184 String subnetId = getSubnetId(vpnName, dstPrefix.getIpv4Address().getValue());
185 if (subnetId == null) {
186 LOG.trace("Can't find corresponding subnet for src IP {}, src MAC {}, dst IP {}, in VPN {}",
187 srcPrefix, mipMacAddress, dstPrefix, vpnName);
190 ip = VpnUtil.getIpPrefix(ip);
191 AdjacencyBuilder newAdjBuilder =
192 new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip))
193 .setPrimaryAdjacency(true).setMacAddress(mipMacAddress).setSubnetId(new Uuid(subnetId))
194 .setPhysNetworkFunc(true);
196 List<Adjacency> adjacencyList = adjacencies.isPresent()
197 ? adjacencies.get().getAdjacency() : new ArrayList<>();
199 adjacencyList.add(newAdjBuilder.build());
201 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
202 VpnInterface newVpnIntf =
203 new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(vpnInterface))
204 .setName(vpnInterface).setVpnInstanceName(vpnName).addAugmentation(Adjacencies.class, aug)
206 VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId, newVpnIntf);
207 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
211 if (adjacencies.isPresent()) {
212 List<Adjacency> adjacencyList = adjacencies.get().getAdjacency();
213 ip = VpnUtil.getIpPrefix(ip);
214 for (Adjacency adjacs : adjacencyList) {
215 if (adjacs.isPrimaryAdjacency()) {
216 nextHopIpAddr = adjacs.getIpAddress();
217 nextHopMacAddress = adjacs.getMacAddress();
221 if (nextHopIpAddr != null) {
222 String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
224 VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
225 VpnUtil.getNextHopLabelKey((rd != null) ? rd : vpnName, ip));
227 LOG.error("Unable to fetch label from Id Manager. Bailing out of adding MIP adjacency {} "
228 + "to vpn interface {} for vpn {}", ip, vpnInterface, vpnName);
231 String nextHopIp = nextHopIpAddr.split("/")[0];
232 AdjacencyBuilder newAdjBuilder =
233 new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip)).setNextHopIpList(
234 Collections.singletonList(nextHopIp));
235 if (mipMacAddress != null && !mipMacAddress.equalsIgnoreCase(nextHopMacAddress)) {
236 newAdjBuilder.setMacAddress(mipMacAddress);
238 adjacencyList.add(newAdjBuilder.build());
239 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
240 VpnInterface newVpnIntf =
241 new VpnInterfaceBuilder().setKey(new VpnInterfaceKey(vpnInterface)).setName(
242 vpnInterface).setVpnInstanceName(vpnName).addAugmentation(Adjacencies.class, aug).build();
243 VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId, newVpnIntf);
244 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
251 private String getSubnetId(String vpnName, String ip) {
252 // Check if this IP belongs to a router_interface
253 VpnPortipToPort vpnPortipToPort =
254 VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ip);
255 if (vpnPortipToPort != null && vpnPortipToPort.isSubnetIp()) {
256 List<Adjacency> adjacecnyList = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker,
257 vpnPortipToPort.getPortName());
258 for (Adjacency adjacency : adjacecnyList) {
259 if (adjacency.isPrimaryAdjacency()) {
260 return adjacency.getSubnetId().getValue();
265 // Check if this IP belongs to a router_gateway
266 List<Uuid> routerIds = VpnUtil.getExternalNetworkRouterIds(dataBroker, new Uuid(vpnName));
267 for (Uuid routerId : routerIds) {
268 Uuid subnetId = VpnUtil.getSubnetFromExternalRouterByIp(dataBroker, routerId, ip);
269 if (subnetId != null) {
270 return subnetId.getValue();
277 private void removeMipAdjacency(String vpnName, String vpnInterface, IpAddress prefix) {
278 String ip = VpnUtil.getIpPrefix(prefix.getIpv4Address().getValue());
279 LOG.trace("Removing {} adjacency from Old VPN Interface {} ", ip, vpnInterface);
280 InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
281 InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
282 synchronized (vpnInterface.intern()) {
283 Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
284 if (adjacencies.isPresent()) {
285 InstanceIdentifier<Adjacency> adjacencyIdentifier =
286 InstanceIdentifier.builder(VpnInterfaces.class).child(VpnInterface.class,
287 new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class).child(Adjacency.class,
288 new AdjacencyKey(ip)).build();
289 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifier);
290 LOG.trace("Successfully Deleted Adjacency into VpnInterface {}", vpnInterface);
295 private void putVpnIpToMigrateArpCache(String vpnName, String ipToQuery, PhysAddress srcMac) {
296 long cacheSize = config.getArpCacheSize().longValue();
297 if (migrateArpCache.size() >= cacheSize) {
298 LOG.debug("ARP_MIGRATE_CACHE: max size {} reached, assuming cache eviction we still put IP {}"
299 + " vpnName {} with MAC {}", cacheSize, ipToQuery, vpnName, srcMac);
301 LOG.debug("ARP_MIGRATE_CACHE: add to dirty cache IP {} vpnName {} with MAC {}", ipToQuery, vpnName, srcMac);
302 migrateArpCache.put(new ImmutablePair<>(vpnName, ipToQuery),
303 new BigInteger(String.valueOf(System.currentTimeMillis())));
306 private boolean isIpInArpMigrateCache(String vpnName, String ipToQuery) {
307 if (migrateArpCache == null || migrateArpCache.size() == 0) {
310 Pair<String, String> keyPair = new ImmutablePair<>(vpnName, ipToQuery);
311 BigInteger prevTimeStampCached = migrateArpCache.getIfPresent(keyPair);
312 if (prevTimeStampCached == null) {
313 LOG.debug("ARP_MIGRATE_CACHE: there is no IP {} vpnName {} in dirty cache, so learn it",
317 if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getArpLearnTimeout()) {
318 LOG.debug("ARP_MIGRATE_CACHE: older than timeout value - remove from dirty cache IP {} vpnName {}",
320 migrateArpCache.invalidate(keyPair);
323 LOG.debug("ARP_MIGRATE_CACHE: younger than timeout value - ignore learning IP {} vpnName {}",