BUG 8880: Trunk port flows not created for subports
[netvirt.git] / vpnservice / neutronvpn / neutronvpn-impl / src / main / java / org / opendaylight / netvirt / neutronvpn / NeutronSubnetGwMacResolver.java
1 /*
2  * Copyright (c) 2016 Hewlett Packard Enterprise, Co. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.neutronvpn;
10
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;
44
45 @Singleton
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;
56
57     @Inject
58     public NeutronSubnetGwMacResolver(final DataBroker broker,
59             final OdlArputilService arputilService, final IElanService elanService,
60             final ICentralizedSwitchProvider cswitchProvider) {
61         this.broker = broker;
62         this.arpUtilService = arputilService;
63         this.elanService = elanService;
64         this.cswitchProvider = cswitchProvider;
65     }
66
67     // TODO Clean up the exception handling
68     @SuppressWarnings("checkstyle:IllegalCatch")
69     @PostConstruct
70     public void init() {
71         LOG.info("{} init", getClass().getSimpleName());
72
73         arpFuture = executorService.scheduleAtFixedRate(() -> {
74             try {
75                 sendArpRequestsToExtGateways();
76             } catch (Throwable t) {
77                 LOG.warn("Failed to send ARP request to GW ips", t);
78             }
79         }, 0, ArpConstants.ARP_CACHE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
80
81     }
82
83     @PreDestroy
84     public void close() {
85         arpFuture.cancel(true);
86     }
87
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);
94     }
95
96     private void sendArpRequestsToExtGateways() {
97         LOG.trace("Sending ARP requests to exteral gateways");
98         for (Router router : NeutronvpnUtils.routerMap.values()) {
99             sendArpRequestsToExtGateways(router);
100         }
101     }
102
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);
108             return;
109         }
110
111         String extInterface = getExternalInterface(router);
112         if (extInterface == null) {
113             LOG.trace("No external interface defined for router {}", router.getUuid().getValue());
114             return;
115         }
116
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());
121             return;
122         }
123
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());
128             return;
129         }
130
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);
136         }
137
138     }
139
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);
146             return;
147         }
148
149         PhysAddress srcMacPhysAddress = new PhysAddress(srcMacAddress.getValue());
150         try {
151             InterfaceAddress interfaceAddress = new InterfaceAddressBuilder().setInterface(interfaceName)
152                     .setIpAddress(srcIpAddress).setMacaddress(srcMacPhysAddress).build();
153
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);
160         }
161     }
162
163     private Port getRouterExtGatewayPort(Router router) {
164         if (router == null) {
165             LOG.trace("Router is null");
166             return null;
167         }
168
169         Uuid extPortId = router.getGatewayPortId();
170         if (extPortId == null) {
171             LOG.trace("Router {} is not associated with any external GW port", router.getUuid().getValue());
172             return null;
173         }
174
175         return NeutronvpnUtils.getNeutronPort(broker, extPortId);
176     }
177
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);
183             return null;
184         }
185
186         Uuid extNetworkId = extGatewayInfo.getExternalNetworkId();
187         if (extNetworkId == null) {
188             LOG.trace("External network id missing for router {}", routerName);
189             return null;
190         }
191
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);
195             return null;
196         }
197
198         return elanService.getExternalElanInterface(extNetworkId.getValue(), primarySwitch);
199     }
200
201     private IpAddress getExternalGwIpAddress(Uuid subnetId) {
202         if (subnetId == null) {
203             LOG.trace("Subnet id is null");
204             return null;
205         }
206
207         Subnet subnet = NeutronvpnUtils.getNeutronSubnet(broker, subnetId);
208         return subnet != null ? subnet.getGatewayIp() : null;
209     }
210
211 }