blob: bfcb2b8c2b05617ecdd4e08c658f919c816ca1c0 [file] [log] [blame]
sangho0c2a3da2016-02-16 13:39:07 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
sangho0c2a3da2016-02-16 13:39:07 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.openstacknetworking.routing;
17
18import com.google.common.collect.Lists;
sangho6032f342016-07-07 14:32:03 +090019import com.google.common.collect.Sets;
sangho0c2a3da2016-02-16 13:39:07 +090020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IPv4;
28import org.onlab.packet.Ip4Address;
sangho6032f342016-07-07 14:32:03 +090029import org.onlab.packet.IpAddress;
sangho0c2a3da2016-02-16 13:39:07 +090030import org.onlab.packet.MacAddress;
Hyunsun Moon0448ea02016-02-23 14:17:39 -080031import org.onlab.packet.UDP;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +090032import org.onlab.util.KryoNamespace;
sangho0c2a3da2016-02-16 13:39:07 +090033import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
sangho6032f342016-07-07 14:32:03 +090035import org.onosproject.mastership.MastershipService;
36import org.onosproject.net.DefaultAnnotations;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090037import org.onosproject.net.DeviceId;
sangho6032f342016-07-07 14:32:03 +090038import org.onosproject.net.Host;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090039import org.onosproject.net.Port;
sangho0c2a3da2016-02-16 13:39:07 +090040import org.onosproject.net.device.DeviceService;
41import org.onosproject.net.driver.DriverService;
42import org.onosproject.net.flowobjective.FlowObjectiveService;
sangho6032f342016-07-07 14:32:03 +090043import org.onosproject.net.host.DefaultHostDescription;
44import org.onosproject.net.host.HostDescription;
45import org.onosproject.net.host.HostEvent;
46import org.onosproject.net.host.HostListener;
47import org.onosproject.net.host.HostProvider;
48import org.onosproject.net.host.HostProviderRegistry;
49import org.onosproject.net.host.HostProviderService;
50import org.onosproject.net.host.HostService;
sangho0c2a3da2016-02-16 13:39:07 +090051import org.onosproject.net.packet.InboundPacket;
52import org.onosproject.net.packet.PacketContext;
53import org.onosproject.net.packet.PacketProcessor;
54import org.onosproject.net.packet.PacketService;
sangho6032f342016-07-07 14:32:03 +090055import org.onosproject.net.provider.AbstractProvider;
56import org.onosproject.net.provider.ProviderId;
sangho93447f12016-02-24 00:33:22 +090057import org.onosproject.openstackinterface.OpenstackFloatingIP;
58import org.onosproject.openstackinterface.OpenstackInterfaceService;
59import org.onosproject.openstackinterface.OpenstackPort;
60import org.onosproject.openstackinterface.OpenstackRouter;
61import org.onosproject.openstackinterface.OpenstackRouterInterface;
sangho0c2a3da2016-02-16 13:39:07 +090062import org.onosproject.openstacknetworking.OpenstackRoutingService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090063import org.onosproject.scalablegateway.api.ScalableGatewayService;
sangho6032f342016-07-07 14:32:03 +090064import org.onosproject.openstacknetworking.routing.OpenstackFloatingIPHandler.Action;
65import org.onosproject.openstacknetworking.Constants;
66import org.onosproject.openstacknode.OpenstackNode;
67import org.onosproject.openstacknode.OpenstackNodeEvent;
68import org.onosproject.openstacknode.OpenstackNodeListener;
69import org.onosproject.openstacknode.OpenstackNodeService;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +090070import org.onosproject.store.serializers.KryoNamespaces;
71import org.onosproject.store.service.ConsistentMap;
72import org.onosproject.store.service.Serializer;
73import org.onosproject.store.service.StorageService;
sangho0c2a3da2016-02-16 13:39:07 +090074import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
77import java.util.Collection;
78import java.util.List;
Daniel Park23193902016-03-24 18:17:19 +090079import java.util.Optional;
sangho6032f342016-07-07 14:32:03 +090080import java.util.Set;
sangho0c2a3da2016-02-16 13:39:07 +090081import java.util.concurrent.ExecutorService;
82import java.util.concurrent.Executors;
83import java.util.stream.Collectors;
84
85import static com.google.common.base.Preconditions.checkNotNull;
86import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moonda27e432016-07-13 13:09:45 -070087import static org.onosproject.net.AnnotationKeys.PORT_NAME;
sangho0c2a3da2016-02-16 13:39:07 +090088
sangho0c2a3da2016-02-16 13:39:07 +090089@Component(immediate = true)
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090090@Service
sangho0c2a3da2016-02-16 13:39:07 +090091/**
92 * Populates flow rules about L3 functionality for VMs in Openstack.
93 */
94public class OpenstackRoutingManager implements OpenstackRoutingService {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090095
96 private final Logger log = LoggerFactory.getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected CoreService coreService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected PacketService packetService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected DeviceService deviceService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +0900108 protected OpenstackInterfaceService openstackService;
sangho0c2a3da2016-02-16 13:39:07 +0900109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected FlowObjectiveService flowObjectiveService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected DriverService driverService;
115
Daniel Park81a61a12016-02-26 08:24:44 +0900116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900117 protected StorageService storageService;
Daniel Park81a61a12016-02-26 08:24:44 +0900118
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho6032f342016-07-07 14:32:03 +0900120 protected HostService hostService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected HostProviderRegistry hostProviderRegistry;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900126 protected ScalableGatewayService gatewayService;
Daniel Park23193902016-03-24 18:17:19 +0900127
sangho6032f342016-07-07 14:32:03 +0900128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected OpenstackNodeService nodeService;
130
131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected MastershipService mastershipService;
133
sangho0c2a3da2016-02-16 13:39:07 +0900134 private ApplicationId appId;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900135 private ConsistentMap<Integer, String> tpPortNumMap; // Map<PortNum, allocated VM`s Mac & destionation Ip address>
136 private ConsistentMap<String, OpenstackFloatingIP> floatingIpMap; // Map<FloatingIp`s Id, FloatingIp object>
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900137 // Map<RouterInterface`s portId, Corresponded port`s network id>
138 private ConsistentMap<String, String> routerInterfaceMap;
sangho6032f342016-07-07 14:32:03 +0900139 private static final ProviderId PID = new ProviderId("of", "org.onosproject.openstackroutering", true);
sangho0c2a3da2016-02-16 13:39:07 +0900140 private static final String APP_ID = "org.onosproject.openstackrouting";
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900141 private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900142 private static final String FLOATING_IP_MAP_NAME = "openstackrouting-floatingip";
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900143 private static final String TP_PORT_MAP_NAME = "openstackrouting-tpportnum";
144 private static final String ROUTER_INTERFACE_MAP_NAME = "openstackrouting-routerinterface";
Daniel Park23193902016-03-24 18:17:19 +0900145 private static final String COLON = ":";
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900146 private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
147 private static final int TP_PORT_MINIMUM_NUM = 1024;
148 private static final int TP_PORT_MAXIMUM_NUM = 65535;
sangho48907542016-03-28 16:07:07 +0900149
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900150 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER = KryoNamespace.newBuilder()
151 .register(KryoNamespaces.API)
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900152 .register(OpenstackFloatingIP.FloatingIpStatus.class)
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700153 .register(OpenstackFloatingIP.class);
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900154
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900155 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700156 .register(KryoNamespaces.API);
sangho0c2a3da2016-02-16 13:39:07 +0900157
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900158 private static final KryoNamespace.Builder ROUTER_INTERFACE_SERIALIZER = KryoNamespace.newBuilder()
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700159 .register(KryoNamespaces.API);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900160
sangho0c2a3da2016-02-16 13:39:07 +0900161 private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
sangho6032f342016-07-07 14:32:03 +0900162 private InternalHostListener internalHostListener = new InternalHostListener();
163 private InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
sangho0c2a3da2016-02-16 13:39:07 +0900164 private ExecutorService l3EventExecutorService =
165 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
166 private ExecutorService icmpEventExecutorService =
167 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
Daniel Park81a61a12016-02-26 08:24:44 +0900168 private ExecutorService arpEventExecutorService =
169 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
170 private OpenstackIcmpHandler openstackIcmpHandler;
171 private OpenstackRoutingArpHandler openstackArpHandler;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900172 private OpenstackRoutingRulePopulator rulePopulator;
sangho6032f342016-07-07 14:32:03 +0900173
174 private HostProviderService hostProviderService;
175 private final HostProvider hostProvider = new InternalHostProvider();
sangho0c2a3da2016-02-16 13:39:07 +0900176
177 @Activate
178 protected void activate() {
179 appId = coreService.registerApplication(APP_ID);
sangho6032f342016-07-07 14:32:03 +0900180 hostService.addListener(internalHostListener);
181 nodeService.addListener(internalNodeListener);
182 hostProviderService = hostProviderRegistry.register(hostProvider);
Daniel Park81a61a12016-02-26 08:24:44 +0900183
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900184 floatingIpMap = storageService.<String, OpenstackFloatingIP>consistentMapBuilder()
185 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
186 .withName(FLOATING_IP_MAP_NAME)
187 .withApplicationId(appId)
188 .build();
189 tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
190 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
191 .withName(TP_PORT_MAP_NAME)
192 .withApplicationId(appId)
193 .build();
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900194 routerInterfaceMap = storageService.<String, String>consistentMapBuilder()
195 .withSerializer(Serializer.using(ROUTER_INTERFACE_SERIALIZER.build()))
196 .withName(ROUTER_INTERFACE_MAP_NAME)
197 .withApplicationId(appId)
198 .build();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900199
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900200 log.info("started");
sangho0c2a3da2016-02-16 13:39:07 +0900201 }
202
203 @Deactivate
204 protected void deactivate() {
205 packetService.removeProcessor(internalPacketProcessor);
sangho6032f342016-07-07 14:32:03 +0900206 hostService.removeListener(internalHostListener);
207 nodeService.removeListener(internalNodeListener);
208
Daniel Park81a61a12016-02-26 08:24:44 +0900209 l3EventExecutorService.shutdown();
210 icmpEventExecutorService.shutdown();
211 arpEventExecutorService.shutdown();
212
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900213 floatingIpMap.clear();
214 tpPortNumMap.clear();
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900215 routerInterfaceMap.clear();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900216
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900217 log.info("stopped");
sangho0c2a3da2016-02-16 13:39:07 +0900218 }
219
220
221 @Override
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900222 public void createFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
223 floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
sangho0c2a3da2016-02-16 13:39:07 +0900224 }
225
226 @Override
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900227 public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
228 if (!floatingIpMap.containsKey(openstackFloatingIp.id())) {
229 log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIp.id());
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900230 return;
231 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900232 if (openstackFloatingIp.portId() == null || openstackFloatingIp.portId().equals("null")) {
233 OpenstackFloatingIP floatingIp = floatingIpMap.get(openstackFloatingIp.id()).value();
sangho6032f342016-07-07 14:32:03 +0900234 // XXX When the VM has been removed, host information has been removed or not ???
235 Optional<Host> host = hostService.getHostsByIp(openstackFloatingIp.fixedIpAddress().getIp4Address())
236 .stream()
237 .findFirst();
238 if (!host.isPresent()) {
239 log.warn("No Host info with the VM IP the Floating IP address {} is found",
240 openstackFloatingIp.floatingIpAddress());
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900241 return;
242 }
243 l3EventExecutorService.execute(
sangho6032f342016-07-07 14:32:03 +0900244 new OpenstackFloatingIPHandler(rulePopulator, floatingIp, Action.DISSASSOCIATE, host.get()));
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900245 floatingIpMap.replace(floatingIp.id(), openstackFloatingIp);
sangho6032f342016-07-07 14:32:03 +0900246 registerFloatingIpToHostService(openstackFloatingIp, Action.DISSASSOCIATE);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900247 } else {
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900248 floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900249 l3EventExecutorService.execute(
sangho6032f342016-07-07 14:32:03 +0900250 new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIp, Action.ASSOCIATE, null));
251 registerFloatingIpToHostService(openstackFloatingIp, Action.ASSOCIATE);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900252 }
sangho0c2a3da2016-02-16 13:39:07 +0900253 }
254
255 @Override
256 public void deleteFloatingIP(String id) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900257 floatingIpMap.remove(id);
sangho0c2a3da2016-02-16 13:39:07 +0900258 }
259
260 @Override
261 public void createRouter(OpenstackRouter openstackRouter) {
sangho0c2a3da2016-02-16 13:39:07 +0900262 }
263
264 @Override
265 public void updateRouter(OpenstackRouter openstackRouter) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900266 if (openstackRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
sanghoefbc0382016-04-06 13:35:38 +0900267 checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900268 } else {
269 unsetExternalConnection();
270 }
271 }
272
273 private void unsetExternalConnection() {
274 Collection<OpenstackRouter> internalRouters = getExternalRouter(false);
275 internalRouters.forEach(r ->
276 getOpenstackRouterInterface(r).forEach(i -> rulePopulator.removeExternalRules(i)));
277 }
278
279 private Collection<OpenstackRouter> getExternalRouter(boolean externalConnection) {
280 List<OpenstackRouter> routers;
281 if (externalConnection) {
282 routers = openstackService.routers()
283 .stream()
284 .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() > 0))
285 .collect(Collectors.toList());
286 } else {
287 routers = openstackService.routers()
288 .stream()
289 .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() == 0))
290 .collect(Collectors.toList());
291 }
292 return routers;
sangho0c2a3da2016-02-16 13:39:07 +0900293 }
294
295 @Override
296 public void deleteRouter(String id) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900297 //TODO : In now, there`s nothing to do for deleteRouter process. It is reserved.
sangho0c2a3da2016-02-16 13:39:07 +0900298 }
299
300 @Override
301 public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
sangho0c2a3da2016-02-16 13:39:07 +0900302 List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
303 routerInterfaces.add(routerInterface);
Daniel Park81a61a12016-02-26 08:24:44 +0900304 checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900305 setL3Connection(getOpenstackRouter(routerInterface.id()), null);
306 routerInterfaceMap.put(routerInterface.portId(), openstackService.port(routerInterface.portId()).networkId());
307 }
308
sanghod177f8f2016-06-29 21:52:23 +0900309 /**
310 * Set flow rules for traffic between two different subnets when more than one subnets
311 * connected to a router.
312 *
313 * @param openstackRouter OpenstackRouter Info
314 * @param openstackPort OpenstackPort Info
315 */
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900316 private void setL3Connection(OpenstackRouter openstackRouter, OpenstackPort openstackPort) {
317 Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(openstackRouter);
sanghod177f8f2016-06-29 21:52:23 +0900318
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900319 if (interfaceList.size() < 2) {
320 return;
321 }
322 if (openstackPort == null) {
323 interfaceList.forEach(i -> {
324 OpenstackPort interfacePort = openstackService.port(i.portId());
325 openstackService.ports()
326 .stream()
327 .filter(p -> p.networkId().equals(interfacePort.networkId())
328 && !p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
329 .forEach(p -> rulePopulator.populateL3Rules(p,
sanghod177f8f2016-06-29 21:52:23 +0900330 getL3ConnectionList(p.networkId(), interfaceList)));
331
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900332 });
333 } else {
334 rulePopulator.populateL3Rules(openstackPort, getL3ConnectionList(openstackPort.networkId(), interfaceList));
335 }
336
337 }
338
339 private List<OpenstackRouterInterface> getL3ConnectionList(String networkId,
340 Collection<OpenstackRouterInterface> interfaceList) {
341 List<OpenstackRouterInterface> targetList = Lists.newArrayList();
342 interfaceList.forEach(i -> {
343 OpenstackPort port = openstackService.port(i.portId());
344 if (!port.networkId().equals(networkId)) {
345 targetList.add(i);
346 }
347 });
348 return targetList;
sangho0c2a3da2016-02-16 13:39:07 +0900349 }
350
351 @Override
352 public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900353 OpenstackRouter router = openstackService.router(routerInterface.id());
354 Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(router);
355 if (interfaceList.size() == 1) {
356 List<OpenstackRouterInterface> newList = Lists.newArrayList();
357 newList.add(routerInterface);
358 interfaceList.forEach(i -> removeL3RulesForRouterInterface(i, router, newList));
359 }
360 removeL3RulesForRouterInterface(routerInterface, router, null);
sangho0c2a3da2016-02-16 13:39:07 +0900361 rulePopulator.removeExternalRules(routerInterface);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900362 routerInterfaceMap.remove(routerInterface.portId());
363 }
364
365 private void removeL3RulesForRouterInterface(OpenstackRouterInterface routerInterface, OpenstackRouter router,
366 List<OpenstackRouterInterface> newList) {
sangho6032f342016-07-07 14:32:03 +0900367 if (!routerInterfaceMap.containsKey(routerInterface.portId())) {
368 log.warn("No router interface information found for {}", routerInterface.portId());
369 return;
370 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900371 openstackService.ports(routerInterfaceMap.get(routerInterface.portId()).value()).forEach(p -> {
372 Ip4Address vmIp = (Ip4Address) p.fixedIps().values().toArray()[0];
373 if (newList == null) {
374 rulePopulator.removeL3Rules(vmIp,
375 getL3ConnectionList(p.networkId(), getOpenstackRouterInterface(router)));
376 } else {
377 rulePopulator.removeL3Rules(vmIp, newList);
378 }
379 }
380 );
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900381 }
382
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900383 @Override
384 public String networkIdForRouterInterface(String portId) {
385 return routerInterfaceMap.get(portId).value();
386 }
387
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900388 private Collection<OpenstackFloatingIP> associatedFloatingIps() {
389 List<OpenstackFloatingIP> fIps = Lists.newArrayList();
390 floatingIpMap.values()
391 .stream()
392 .filter(fIp -> fIp.value().portId() != null)
393 .forEach(fIp -> fIps.add(fIp.value()));
394 return fIps;
sangho0c2a3da2016-02-16 13:39:07 +0900395 }
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900396
397 private void reloadInitL3Rules() {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900398
sanghod177f8f2016-06-29 21:52:23 +0900399 l3EventExecutorService.execute(() ->
400 openstackService.ports()
401 .stream()
402 .forEach(p ->
403 {
404 if (p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) {
405 updateRouterInterface(portToRouterInterface(p));
406 } else {
407 Optional<Ip4Address> vmIp = p.fixedIps().values().stream().findAny();
408 if (vmIp.isPresent()) {
409 OpenstackFloatingIP floatingIP = getOpenstackFloatingIp(vmIp.get());
410 if (floatingIP != null) {
411 updateFloatingIP(floatingIP);
412 }
413 }
414 }
415 })
416 );
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900417 }
418
419 private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
420 OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
Daniel Park81a61a12016-02-26 08:24:44 +0900421 .id(checkNotNull(p.deviceId()))
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900422 .tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
423 .subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
Daniel Park81a61a12016-02-26 08:24:44 +0900424 .portId(checkNotNull(p.id()));
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900425
426 return osBuilder.build();
427 }
428
sangho0c2a3da2016-02-16 13:39:07 +0900429 private class InternalPacketProcessor implements PacketProcessor {
430
431 @Override
432 public void process(PacketContext context) {
433
sangho6032f342016-07-07 14:32:03 +0900434 DeviceId senderDeviceId = context.inPacket().receivedFrom().deviceId();
435 if (!nodeService.routerBridge(senderDeviceId).isPresent()) {
436 log.warn("No router bridge for {} is found.", senderDeviceId);
437 return;
438 }
sangho0c2a3da2016-02-16 13:39:07 +0900439 if (context.isHandled()) {
440 return;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900441 } else if (!checkGatewayNode(context.inPacket().receivedFrom().deviceId())) {
Daniel Park81a61a12016-02-26 08:24:44 +0900442 return;
sangho0c2a3da2016-02-16 13:39:07 +0900443 }
444
445 InboundPacket pkt = context.inPacket();
446 Ethernet ethernet = pkt.parsed();
447
Daniel Park81a61a12016-02-26 08:24:44 +0900448 //TODO: Considers IPv6 later.
449 if (ethernet == null) {
450 return;
451 } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
sangho0c2a3da2016-02-16 13:39:07 +0900452 IPv4 iPacket = (IPv4) ethernet.getPayload();
sangho0c2a3da2016-02-16 13:39:07 +0900453 switch (iPacket.getProtocol()) {
454 case IPv4.PROTOCOL_ICMP:
Daniel Park81a61a12016-02-26 08:24:44 +0900455
Daniel Park23193902016-03-24 18:17:19 +0900456 icmpEventExecutorService.execute(() ->
Daniel Park81a61a12016-02-26 08:24:44 +0900457 openstackIcmpHandler.processIcmpPacket(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900458 break;
Hyunsun Moon0448ea02016-02-23 14:17:39 -0800459 case IPv4.PROTOCOL_UDP:
460 // don't process DHCP
461 UDP udpPacket = (UDP) iPacket.getPayload();
462 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
463 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
464 break;
465 }
sangho0c2a3da2016-02-16 13:39:07 +0900466 default:
467 int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900468 DeviceId deviceId = pkt.receivedFrom().deviceId();
469 Port port = null;
470 port = deviceService.getPort(deviceId,
Kyuhwi Choi176c83d2016-07-14 11:39:37 +0900471 gatewayService.getGatewayExternalPort(deviceId));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900472 if (port != null) {
Daniel Park23193902016-03-24 18:17:19 +0900473 OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(),
474 Ip4Address.valueOf(iPacket.getSourceAddress()));
475 l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context,
sangho6032f342016-07-07 14:32:03 +0900476 portNum, openstackPort, port));
477
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900478 } else {
479 log.warn("There`s no external interface");
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900480 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900481
sangho0c2a3da2016-02-16 13:39:07 +0900482 break;
483 }
Daniel Park81a61a12016-02-26 08:24:44 +0900484 } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Daniel Park23193902016-03-24 18:17:19 +0900485 arpEventExecutorService.execute(() ->
Daniel Park81a61a12016-02-26 08:24:44 +0900486 openstackArpHandler.processArpPacketFromRouter(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900487 }
488 }
489
490 private int getPortNum(MacAddress sourceMac, int destinationAddress) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900491 int portNum = findUnusedPortNum();
492 if (portNum == 0) {
493 clearPortNumMap();
494 portNum = findUnusedPortNum();
495 }
Daniel Park23193902016-03-24 18:17:19 +0900496 tpPortNumMap.put(portNum, sourceMac.toString().concat(COLON).concat(String.valueOf(destinationAddress)));
sangho0c2a3da2016-02-16 13:39:07 +0900497 return portNum;
498 }
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900499
500 private int findUnusedPortNum() {
501 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
502 if (!tpPortNumMap.containsKey(i)) {
503 return i;
504 }
505 }
506 return 0;
507 }
508
509 }
510
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900511 private boolean checkGatewayNode(DeviceId deviceId) {
512 return gatewayService.getGatewayDeviceIds().contains(deviceId);
513 }
514
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900515 private void clearPortNumMap() {
516 tpPortNumMap.entrySet().forEach(e -> {
517 if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
518 tpPortNumMap.remove(e.getKey());
519 }
520 });
sangho0c2a3da2016-02-16 13:39:07 +0900521 }
522
Daniel Park23193902016-03-24 18:17:19 +0900523 private Optional<Port> getExternalPort(DeviceId deviceId, String interfaceName) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900524 return deviceService.getPorts(deviceId)
525 .stream()
526 .filter(p -> p.annotations().value(PORT_NAME).equals(interfaceName))
Daniel Park23193902016-03-24 18:17:19 +0900527 .findAny();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900528 }
529
sangho0c2a3da2016-02-16 13:39:07 +0900530 private void checkExternalConnection(OpenstackRouter router,
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900531 Collection<OpenstackRouterInterface> interfaces) {
sangho0c2a3da2016-02-16 13:39:07 +0900532 checkNotNull(router, "Router can not be null");
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900533 checkNotNull(interfaces, "routerInterfaces can not be null");
sangho0c2a3da2016-02-16 13:39:07 +0900534 Ip4Address externalIp = router.gatewayExternalInfo().externalFixedIps()
535 .values().stream().findFirst().orElse(null);
536 if ((externalIp == null) || (!router.gatewayExternalInfo().isEnablePnat())) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900537 log.debug("Not satisfied to set pnat configuration");
sangho0c2a3da2016-02-16 13:39:07 +0900538 return;
539 }
Daniel Park23193902016-03-24 18:17:19 +0900540 interfaces.forEach(this::initiateL3Rule);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900541 }
542
Daniel Park23193902016-03-24 18:17:19 +0900543 private Optional<OpenstackRouter> getRouterfromExternalIp(Ip4Address externalIp) {
544 return getExternalRouter(true)
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900545 .stream()
546 .filter(r -> r.gatewayExternalInfo()
547 .externalFixedIps()
548 .values()
549 .stream()
Daniel Park23193902016-03-24 18:17:19 +0900550 .findAny()
551 .get()
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900552 .equals(externalIp))
Daniel Park23193902016-03-24 18:17:19 +0900553 .findAny();
sangho0c2a3da2016-02-16 13:39:07 +0900554 }
555
Daniel Park23193902016-03-24 18:17:19 +0900556 private void initiateL3Rule(OpenstackRouterInterface routerInterface) {
sangho0c2a3da2016-02-16 13:39:07 +0900557 long vni = Long.parseLong(openstackService.network(openstackService
Daniel Park81a61a12016-02-26 08:24:44 +0900558 .port(routerInterface.portId()).networkId()).segmentId());
Daniel Park23193902016-03-24 18:17:19 +0900559 rulePopulator.populateExternalRules(vni);
sangho0c2a3da2016-02-16 13:39:07 +0900560 }
561
562 private Collection<OpenstackRouterInterface> getOpenstackRouterInterface(OpenstackRouter router) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900563 List<OpenstackRouterInterface> interfaces = Lists.newArrayList();
564 openstackService.ports()
565 .stream()
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900566 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
567 && p.deviceId().equals(router.id()))
568 .forEach(p -> interfaces.add(portToRouterInterface(p)));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900569 return interfaces;
sangho0c2a3da2016-02-16 13:39:07 +0900570 }
571
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900572 private OpenstackRouter getOpenstackRouter(String id) {
sangho0c2a3da2016-02-16 13:39:07 +0900573 return openstackService.routers().stream().filter(r ->
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900574 r.id().equals(id)).iterator().next();
sangho0c2a3da2016-02-16 13:39:07 +0900575 }
576
577 private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900578 OpenstackPort openstackPort = openstackService.ports().stream()
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900579 .filter(p -> p.macAddress().equals(sourceMac)).iterator().next();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900580 return openstackPort.fixedIps().values().stream().filter(ip ->
581 ip.equals(ip4Address)).count() > 0 ? openstackPort : null;
sangho0c2a3da2016-02-16 13:39:07 +0900582 }
583
sanghod177f8f2016-06-29 21:52:23 +0900584 private OpenstackFloatingIP getOpenstackFloatingIp(Ip4Address vmIp) {
585 Optional<OpenstackFloatingIP> floatingIp = floatingIpMap.asJavaMap().values().stream()
586 .filter(f -> f.portId() != null && f.fixedIpAddress().equals(vmIp))
587 .findAny();
588
589 if (floatingIp.isPresent()) {
590 return floatingIp.get();
591 }
592 log.debug("There is no floating IP information for VM IP {}", vmIp);
593
594 return null;
595 }
596
sangho6032f342016-07-07 14:32:03 +0900597 private Optional<OpenstackPort> getRouterInterfacePort(String networkId) {
598
599 return openstackService.ports()
600 .stream()
601 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
602 && p.networkId().equals(networkId))
603 .findAny();
604 }
605
606 // TODO: Remove the function and the related codes when vRouter is running on different ONOS instance.
607 private void registerFloatingIpToHostService(OpenstackFloatingIP openstackFloatingIp, Action action) {
608
609 Optional<Host> hostOptional = hostService.getHostsByIp(openstackFloatingIp.fixedIpAddress())
610 .stream()
611 .findFirst();
612 if (!hostOptional.isPresent()) {
613 log.warn("No host with IP {} is registered and cannot add the floating IP. ",
614 openstackFloatingIp.floatingIpAddress());
Daniel Park81a61a12016-02-26 08:24:44 +0900615 return;
616 }
617
sangho6032f342016-07-07 14:32:03 +0900618 Host host = hostOptional.get();
619 Set<IpAddress> ipAddresses = Sets.newHashSet();
620 if (action == Action.ASSOCIATE) {
621 ipAddresses.add(openstackFloatingIp.floatingIpAddress());
622 }
sanghod177f8f2016-06-29 21:52:23 +0900623
sangho6032f342016-07-07 14:32:03 +0900624 HostDescription hostDescription =
625 new DefaultHostDescription(host.mac(), host.vlan(), host.location(), ipAddresses,
626 (DefaultAnnotations) host.annotations());
sanghod177f8f2016-06-29 21:52:23 +0900627
sangho6032f342016-07-07 14:32:03 +0900628 hostProviderService.hostDetected(host.id(), hostDescription, false);
Daniel Park81a61a12016-02-26 08:24:44 +0900629 }
630
sangho6032f342016-07-07 14:32:03 +0900631 private class InternalHostListener implements HostListener {
Daniel Park81a61a12016-02-26 08:24:44 +0900632
sangho6032f342016-07-07 14:32:03 +0900633 private void hostDetected(Host host) {
sangho6032f342016-07-07 14:32:03 +0900634 String portId = host.annotations().value(Constants.PORT_ID);
635 OpenstackPort openstackPort = openstackService.port(portId);
636 if (openstackPort == null) {
637 log.warn("No OpenstackPort information found from OpenStack for port ID {}", portId);
Daniel Park81a61a12016-02-26 08:24:44 +0900638 return;
639 }
640
sangho6032f342016-07-07 14:32:03 +0900641 Optional<OpenstackPort> routerPort = getRouterInterfacePort(openstackPort.networkId());
642 if (routerPort.isPresent()) {
643 OpenstackRouterInterface routerInterface = portToRouterInterface(routerPort.get());
644 l3EventExecutorService.execute(() ->
645 setL3Connection(getOpenstackRouter(routerInterface.id()), openstackPort));
646
647 }
648 }
649
650 private void hostRemoved(Host host) {
651 String portId = host.annotations().value(Constants.PORT_ID);
652 OpenstackPort openstackPort = openstackService.port(portId);
653 if (openstackPort == null) {
654 log.warn("No OpenstackPort information found from OpenStack for port ID {}", portId);
655 return;
656 }
657
658 Optional<OpenstackPort> routerPort = getRouterInterfacePort(openstackPort.networkId());
659 if (routerPort.isPresent()) {
660 OpenstackRouterInterface routerInterface = portToRouterInterface(routerPort.get());
661 IpAddress ipAddress = host.ipAddresses().stream().findFirst().get();
662 l3EventExecutorService.execute(() -> rulePopulator.removeL3Rules(ipAddress.getIp4Address(),
663 getL3ConnectionList(host.annotations().value(Constants.NETWORK_ID),
664 getOpenstackRouterInterface(getOpenstackRouter(routerInterface.id())))));
665 }
666 }
667
668 private boolean isValidHost(Host host) {
669 return !host.ipAddresses().isEmpty() &&
670 host.annotations().value(Constants.VXLAN_ID) != null &&
671 host.annotations().value(Constants.NETWORK_ID) != null &&
672 host.annotations().value(Constants.TENANT_ID) != null &&
673 host.annotations().value(Constants.PORT_ID) != null;
674 }
675
676 @Override
677 public void event(HostEvent event) {
678 Host host = event.subject();
679 if (!mastershipService.isLocalMaster(host.location().deviceId())) {
680 // do not allow to proceed without mastership
681 return;
682 }
683
684 if (!isValidHost(host)) {
685 log.debug("Invalid host event, ignore it {}", host);
686 return;
687 }
688
689 switch (event.type()) {
690 case HOST_UPDATED:
691 case HOST_ADDED:
692 l3EventExecutorService.execute(() -> hostDetected(host));
693 break;
694 case HOST_REMOVED:
695 l3EventExecutorService.execute(() -> hostRemoved(host));
696 break;
697 default:
698 break;
Daniel Park81a61a12016-02-26 08:24:44 +0900699 }
700 }
701 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900702
sangho6032f342016-07-07 14:32:03 +0900703 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
704
705 private void nodeComplete() {
706
707 rulePopulator = new OpenstackRoutingRulePopulator(appId, openstackService, flowObjectiveService,
708 deviceService, driverService, nodeService, gatewayService);
709 openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService, hostService,
710 openstackService, nodeService, gatewayService);
711 openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, nodeService,
712 gatewayService);
713
714 // Packet handlers must be started AFTER all initialization processes.
715 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
716
717 openstackIcmpHandler.requestPacket(appId);
718 openstackArpHandler.requestPacket(appId);
719
720 openstackService.floatingIps().stream()
721 .forEach(f -> floatingIpMap.put(f.id(), f));
722
723 reloadInitL3Rules();
724 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900725
726 @Override
sangho6032f342016-07-07 14:32:03 +0900727 public void event(OpenstackNodeEvent event) {
728 OpenstackNode node = event.node();
729
730 switch (event.type()) {
731 case COMPLETE:
732 log.info("COMPLETE node {} detected", node.hostname());
733 l3EventExecutorService.execute(() -> nodeComplete());
734 break;
735 case INCOMPLETE:
736 break;
737 default:
738 break;
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900739 }
sangho6032f342016-07-07 14:32:03 +0900740 }
741 }
742
743 private class InternalHostProvider extends AbstractProvider implements HostProvider {
744
745 /**
746 * Creates a provider with the supplier identifier.
747 */
748 protected InternalHostProvider() {
749 super(PID);
750 }
751
752 @Override
753 public void triggerProbe(Host host) {
754 // nothing to do
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900755 }
756 }
sangho0c2a3da2016-02-16 13:39:07 +0900757}