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.JdkFutureAdapters;
12 import com.google.common.util.concurrent.ThreadFactoryBuilder;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.Future;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.TimeUnit;
22 import javax.annotation.PostConstruct;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.genius.arputil.api.ArpConstants;
28 import org.opendaylight.genius.mdsalutil.NWUtil;
29 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
30 import org.opendaylight.netvirt.elanmanager.api.IElanService;
31 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationOutput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.ExternalGatewayInfo;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIpsKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
52 import org.opendaylight.yangtools.yang.common.RpcResult;
53 import org.opendaylight.yangtools.yang.common.Uint64;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 public class NeutronSubnetGwMacResolver {
59 private static final Logger LOG = LoggerFactory.getLogger(NeutronSubnetGwMacResolver.class);
60 private static final long L3_INSTALL_DELAY_MILLIS = 5000;
62 private final OdlArputilService arpUtilService;
63 private final IElanService elanService;
64 private final ICentralizedSwitchProvider cswitchProvider;
65 private final NeutronvpnUtils neutronvpnUtils;
66 private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(
67 new ThreadFactoryBuilder().setNameFormat("Gw-Mac-Res").build());
68 private final Ipv6NdUtilService ipv6NdUtilService;
71 public NeutronSubnetGwMacResolver(final OdlArputilService arputilService, final IElanService elanService,
72 final ICentralizedSwitchProvider cswitchProvider, final NeutronvpnUtils neutronvpnUtils,
73 final Ipv6NdUtilService ipv6NdUtilService) {
74 this.arpUtilService = arputilService;
75 this.elanService = elanService;
76 this.cswitchProvider = cswitchProvider;
77 this.neutronvpnUtils = neutronvpnUtils;
78 this.ipv6NdUtilService = ipv6NdUtilService;
81 // TODO Clean up the exception handling
82 @SuppressWarnings("checkstyle:IllegalCatch")
85 LOG.info("{} init", getClass().getSimpleName());
87 executorService.scheduleAtFixedRate(() -> {
89 sendArpRequestsToExtGateways();
90 } catch (Exception e) {
91 LOG.warn("Failed to send ARP request to GW ips", e);
93 }, 0, ArpConstants.ARP_CACHE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
99 executorService.shutdownNow();
102 public void sendArpRequestsToExtGateways(Router router) {
103 // Let the FIB flows a chance to be installed
104 // otherwise the ARP response will be routed straight to L2
105 // and bypasses L3 arp cache
106 executorService.schedule(() -> sendArpRequestsToExtGatewayTask(router), L3_INSTALL_DELAY_MILLIS,
107 TimeUnit.MILLISECONDS);
110 public void sendArpRequestsToExtGateways() {
111 LOG.trace("Sending ARP requests to external gateways");
112 for (Router router : neutronvpnUtils.getAllRouters()) {
113 sendArpRequestsToExtGateways(router);
117 private void sendArpRequestsToExtGatewayTask(Router router) {
118 LOG.trace("Send ARP requests to external GW for router {}", router);
119 Port extPort = getRouterExtGatewayPort(router);
120 if (extPort == null) {
121 LOG.trace("External GW port for router {} is missing", router.getUuid().getValue());
125 String extInterface = getExternalInterface(router);
126 if (extInterface == null) {
127 LOG.trace("No external interface defined for router {}", router.getUuid().getValue());
131 Map<FixedIpsKey, FixedIps> keyFixedIpsMap = extPort.getFixedIps();
132 if (keyFixedIpsMap == null || keyFixedIpsMap.isEmpty()) {
133 LOG.trace("External GW port {} for router {} has no fixed IPs", extPort.getUuid().getValue(),
134 router.getUuid().getValue());
138 MacAddress macAddress = extPort.getMacAddress();
139 if (macAddress == null) {
140 LOG.trace("External GW port {} for router {} has no mac address", extPort.getUuid().getValue(),
141 router.getUuid().getValue());
145 for (FixedIps fixIp : keyFixedIpsMap.values()) {
146 Uuid subnetId = fixIp.getSubnetId();
147 IpAddress srcIpAddress = fixIp.getIpAddress();
148 IpAddress dstIpAddress = getExternalGwIpAddress(subnetId);
149 String srcIpAddressString = srcIpAddress.stringValue();
150 String dstIpAddressString = dstIpAddress.stringValue();
151 if (NWUtil.isIpv4Address(srcIpAddressString)) {
152 sendArpRequest(srcIpAddress, dstIpAddress, macAddress, extInterface);
154 sendNeighborSolication(new Ipv6Address(srcIpAddressString),macAddress,
155 new Ipv6Address(dstIpAddressString), extInterface);
161 // TODO Clean up the exception handling
162 @SuppressWarnings("checkstyle:IllegalCatch")
163 private void sendArpRequest(IpAddress srcIpAddress, IpAddress dstIpAddress, MacAddress srcMacAddress,
164 String interfaceName) {
165 if (srcIpAddress == null || dstIpAddress == null) {
166 LOG.trace("Skip sending ARP to external GW srcIp {} dstIp {}", srcIpAddress, dstIpAddress);
170 PhysAddress srcMacPhysAddress = new PhysAddress(srcMacAddress.getValue());
172 InterfaceAddress interfaceAddress = new InterfaceAddressBuilder().setInterface(interfaceName)
173 .setIpAddress(srcIpAddress).setMacaddress(srcMacPhysAddress).build();
175 SendArpRequestInput sendArpRequestInput = new SendArpRequestInputBuilder().setIpaddress(dstIpAddress)
176 .setInterfaceAddress(Collections.singletonList(interfaceAddress)).build();
178 ListenableFutures.addErrorLogging(JdkFutureAdapters.listenInPoolThread(
179 arpUtilService.sendArpRequest(sendArpRequestInput)), LOG, "Send ARP request");
180 } catch (Exception e) {
181 LOG.error("Failed to send ARP request to external GW {} from interface {}",
182 dstIpAddress.getIpv4Address().getValue(), interfaceName, e);
186 private void sendNeighborSolication(Ipv6Address srcIpv6Address,
187 MacAddress srcMac, Ipv6Address dstIpv6Address, String interfaceName) {
188 List<org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
189 .nd.util.rev170210.interfaces.InterfaceAddress> interfaceAddresses = new ArrayList<>();
190 interfaceAddresses.add(new org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
191 .nd.util.rev170210.interfaces.InterfaceAddressBuilder()
192 .setInterface(interfaceName)
193 .setSrcIpAddress(srcIpv6Address)
194 .setSrcMacAddress(new PhysAddress(srcMac.getValue())).build());
195 SendNeighborSolicitationInput input = new SendNeighborSolicitationInputBuilder()
196 .setInterfaceAddress(interfaceAddresses).setTargetIpAddress(dstIpv6Address)
199 Future<RpcResult<SendNeighborSolicitationOutput>> result = ipv6NdUtilService
200 .sendNeighborSolicitation(input);
201 RpcResult<SendNeighborSolicitationOutput> rpcResult = result.get();
202 if (!rpcResult.isSuccessful()) {
203 LOG.error("sendNeighborSolicitationToOfGroup: RPC Call failed for input={} and Errors={}", input,
204 rpcResult.getErrors());
206 } catch (InterruptedException | ExecutionException e) {
207 LOG.error("Failed to send NS packet to ELAN group, input={}", input, e);
212 private Port getRouterExtGatewayPort(Router router) {
213 if (router == null) {
214 LOG.trace("Router is null");
218 Uuid extPortId = router.getGatewayPortId();
219 if (extPortId == null) {
220 LOG.trace("Router {} is not associated with any external GW port", router.getUuid().getValue());
224 return neutronvpnUtils.getNeutronPort(extPortId);
228 private String getExternalInterface(Router router) {
229 ExternalGatewayInfo extGatewayInfo = router.getExternalGatewayInfo();
230 String routerName = router.getUuid().getValue();
231 if (extGatewayInfo == null) {
232 LOG.warn("External GW info missing for router {}", routerName);
236 Uuid extNetworkId = extGatewayInfo.getExternalNetworkId();
237 if (extNetworkId == null) {
238 LOG.warn("External network id missing for router {}", routerName);
242 Uint64 primarySwitch = cswitchProvider.getPrimarySwitchForRouter(routerName);
243 if (primarySwitch == null || Uint64.ZERO.equals(primarySwitch)) {
244 LOG.warn("Primary switch has not been allocated for router {}", routerName);
248 return elanService.getExternalElanInterface(extNetworkId.getValue(), primarySwitch);
252 private IpAddress getExternalGwIpAddress(Uuid subnetId) {
253 if (subnetId == null) {
254 LOG.error("Subnet id is null");
258 Subnet subnet = neutronvpnUtils.getNeutronSubnet(subnetId);
259 return subnet != null ? subnet.getGatewayIp() : null;