2 * Copyright (c) 2016 Hewlett Packard Enterprise, Co. 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
9 package org.opendaylight.netvirt.neutronvpn;
11 import com.google.common.util.concurrent.ThreadFactoryBuilder;
12 import java.math.BigInteger;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.ScheduledExecutorService;
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.ThreadFactory;
19 import java.util.concurrent.TimeUnit;
20 import javax.annotation.PostConstruct;
21 import javax.annotation.PreDestroy;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.genius.arputil.api.ArpConstants;
26 import org.opendaylight.netvirt.elanmanager.api.IElanService;
27 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.ExternalGatewayInfo;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 public class NeutronSubnetGwMacResolver {
47 private static final Logger LOG = LoggerFactory.getLogger(NeutronSubnetGwMacResolver.class);
48 private static final long L3_INSTALL_DELAY_MILLIS = 5000;
49 private final DataBroker broker;
50 private final OdlArputilService arpUtilService;
51 private final IElanService elanService;
52 private final ICentralizedSwitchProvider cswitchProvider;
53 private final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Gw-Mac-Res").build();
54 private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(threadFactory);
55 private ScheduledFuture<?> arpFuture;
58 public NeutronSubnetGwMacResolver(final DataBroker broker,
59 final OdlArputilService arputilService, final IElanService elanService,
60 final ICentralizedSwitchProvider cswitchProvider) {
62 this.arpUtilService = arputilService;
63 this.elanService = elanService;
64 this.cswitchProvider = cswitchProvider;
67 // TODO Clean up the exception handling
68 @SuppressWarnings("checkstyle:IllegalCatch")
71 LOG.info("{} init", getClass().getSimpleName());
73 arpFuture = executorService.scheduleAtFixedRate(() -> {
75 sendArpRequestsToExtGateways();
76 } catch (Throwable t) {
77 LOG.warn("Failed to send ARP request to GW ips", t);
79 }, 0, ArpConstants.ARP_CACHE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
85 arpFuture.cancel(true);
88 public void sendArpRequestsToExtGateways(Router router) {
89 // Let the FIB flows a chance to be installed
90 // otherwise the ARP response will be routed straight to L2
91 // and bypasses L3 arp cache
92 executorService.schedule(() -> sendArpRequestsToExtGatewayTask(router), L3_INSTALL_DELAY_MILLIS,
93 TimeUnit.MILLISECONDS);
96 private void sendArpRequestsToExtGateways() {
97 LOG.trace("Sending ARP requests to exteral gateways");
98 for (Router router : NeutronvpnUtils.routerMap.values()) {
99 sendArpRequestsToExtGateways(router);
103 private void sendArpRequestsToExtGatewayTask(Router router) {
104 LOG.trace("Send ARP requests to external GW for router {}", router);
105 Port extPort = getRouterExtGatewayPort(router);
106 if (extPort == null) {
107 LOG.trace("External GW port for router {} is missing", router);
111 String extInterface = getExternalInterface(router);
112 if (extInterface == null) {
113 LOG.trace("No external interface defined for router {}", router.getUuid().getValue());
117 List<FixedIps> fixedIps = extPort.getFixedIps();
118 if (fixedIps == null || fixedIps.isEmpty()) {
119 LOG.trace("External GW port {} for router {} has no fixed IPs", extPort.getUuid().getValue(),
120 router.getUuid().getValue());
124 MacAddress macAddress = extPort.getMacAddress();
125 if (macAddress == null) {
126 LOG.trace("External GW port {} for router {} has no mac address", extPort.getUuid().getValue(),
127 router.getUuid().getValue());
131 for (FixedIps fixIp : fixedIps) {
132 Uuid subnetId = fixIp.getSubnetId();
133 IpAddress srcIpAddress = fixIp.getIpAddress();
134 IpAddress dstIpAddress = getExternalGwIpAddress(subnetId);
135 sendArpRequest(srcIpAddress, dstIpAddress, macAddress, extInterface);
140 // TODO Clean up the exception handling
141 @SuppressWarnings("checkstyle:IllegalCatch")
142 private void sendArpRequest(IpAddress srcIpAddress, IpAddress dstIpAddress, MacAddress srcMacAddress,
143 String interfaceName) {
144 if (srcIpAddress == null || dstIpAddress == null) {
145 LOG.trace("Skip sending ARP to external GW srcIp {} dstIp {}", srcIpAddress, dstIpAddress);
149 PhysAddress srcMacPhysAddress = new PhysAddress(srcMacAddress.getValue());
151 InterfaceAddress interfaceAddress = new InterfaceAddressBuilder().setInterface(interfaceName)
152 .setIpAddress(srcIpAddress).setMacaddress(srcMacPhysAddress).build();
154 SendArpRequestInput sendArpRequestInput = new SendArpRequestInputBuilder().setIpaddress(dstIpAddress)
155 .setInterfaceAddress(Collections.singletonList(interfaceAddress)).build();
156 arpUtilService.sendArpRequest(sendArpRequestInput);
157 } catch (Exception e) {
158 LOG.error("Failed to send ARP request to external GW {} from interface {}",
159 dstIpAddress.getIpv4Address().getValue(), interfaceName, e);
163 private Port getRouterExtGatewayPort(Router router) {
164 if (router == null) {
165 LOG.trace("Router is null");
169 Uuid extPortId = router.getGatewayPortId();
170 if (extPortId == null) {
171 LOG.trace("Router {} is not associated with any external GW port", router.getUuid().getValue());
175 return NeutronvpnUtils.getNeutronPort(broker, extPortId);
178 private String getExternalInterface(Router router) {
179 ExternalGatewayInfo extGatewayInfo = router.getExternalGatewayInfo();
180 String routerName = router.getUuid().getValue();
181 if (extGatewayInfo == null) {
182 LOG.trace("External GW info missing for router {}", routerName);
186 Uuid extNetworkId = extGatewayInfo.getExternalNetworkId();
187 if (extNetworkId == null) {
188 LOG.trace("External network id missing for router {}", routerName);
192 BigInteger primarySwitch = cswitchProvider.getPrimarySwitchForRouter(routerName);
193 if (primarySwitch == null || BigInteger.ZERO.equals(primarySwitch)) {
194 LOG.trace("Primary switch has not been allocated for router {}", routerName);
198 return elanService.getExternalElanInterface(extNetworkId.getValue(), primarySwitch);
201 private IpAddress getExternalGwIpAddress(Uuid subnetId) {
202 if (subnetId == null) {
203 LOG.trace("Subnet id is null");
207 Subnet subnet = NeutronvpnUtils.getNeutronSubnet(broker, subnetId);
208 return subnet != null ? subnet.getGatewayIp() : null;