blob: 406179aacc69973506a0564bf67e8ea1103418a6 [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;
sangho0c2a3da2016-02-16 13:39:07 +090019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IPv4;
27import org.onlab.packet.Ip4Address;
28import org.onlab.packet.MacAddress;
Hyunsun Moon0448ea02016-02-23 14:17:39 -080029import org.onlab.packet.UDP;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +090030import org.onlab.util.KryoNamespace;
sangho0c2a3da2016-02-16 13:39:07 +090031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090033import org.onosproject.net.DeviceId;
34import org.onosproject.net.Port;
Daniel Park81a61a12016-02-26 08:24:44 +090035import org.onosproject.net.config.ConfigFactory;
36import org.onosproject.net.config.NetworkConfigEvent;
37import org.onosproject.net.config.NetworkConfigListener;
38import org.onosproject.net.config.NetworkConfigRegistry;
39import org.onosproject.net.config.NetworkConfigService;
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +090040import org.onosproject.net.device.DeviceEvent;
41import org.onosproject.net.device.DeviceListener;
sangho0c2a3da2016-02-16 13:39:07 +090042import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.driver.DriverService;
44import org.onosproject.net.flowobjective.FlowObjectiveService;
45import org.onosproject.net.packet.InboundPacket;
46import org.onosproject.net.packet.PacketContext;
47import org.onosproject.net.packet.PacketProcessor;
48import org.onosproject.net.packet.PacketService;
sangho93447f12016-02-24 00:33:22 +090049import org.onosproject.openstackinterface.OpenstackFloatingIP;
50import org.onosproject.openstackinterface.OpenstackInterfaceService;
51import org.onosproject.openstackinterface.OpenstackPort;
52import org.onosproject.openstackinterface.OpenstackRouter;
53import org.onosproject.openstackinterface.OpenstackRouterInterface;
sangho48907542016-03-28 16:07:07 +090054import org.onosproject.openstacknetworking.OpenstackNetworkingConfig;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +090055import org.onosproject.openstacknetworking.OpenstackPortInfo;
sangho0c2a3da2016-02-16 13:39:07 +090056import org.onosproject.openstacknetworking.OpenstackRoutingService;
sangho48907542016-03-28 16:07:07 +090057import org.onosproject.openstacknetworking.OpenstackSubjectFactories;
Daniel Park81a61a12016-02-26 08:24:44 +090058import org.onosproject.openstacknetworking.OpenstackSwitchingService;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +090059import org.onosproject.store.serializers.KryoNamespaces;
60import org.onosproject.store.service.ConsistentMap;
61import org.onosproject.store.service.Serializer;
62import org.onosproject.store.service.StorageService;
sangho0c2a3da2016-02-16 13:39:07 +090063import org.slf4j.Logger;
64import org.slf4j.LoggerFactory;
65
66import java.util.Collection;
67import java.util.List;
Daniel Park23193902016-03-24 18:17:19 +090068import java.util.Map;
69import java.util.Optional;
sangho0c2a3da2016-02-16 13:39:07 +090070import java.util.concurrent.ExecutorService;
71import java.util.concurrent.Executors;
72import java.util.stream.Collectors;
73
74import static com.google.common.base.Preconditions.checkNotNull;
75import static org.onlab.util.Tools.groupedThreads;
76
sangho0c2a3da2016-02-16 13:39:07 +090077@Component(immediate = true)
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090078@Service
sangho0c2a3da2016-02-16 13:39:07 +090079/**
80 * Populates flow rules about L3 functionality for VMs in Openstack.
81 */
82public class OpenstackRoutingManager implements OpenstackRoutingService {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090083
84 private final Logger log = LoggerFactory.getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090085
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected CoreService coreService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected PacketService packetService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected DeviceService deviceService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +090096 protected OpenstackInterfaceService openstackService;
sangho0c2a3da2016-02-16 13:39:07 +090097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Daniel Park81a61a12016-02-26 08:24:44 +090099 protected OpenstackSwitchingService openstackSwitchingService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho0c2a3da2016-02-16 13:39:07 +0900102 protected FlowObjectiveService flowObjectiveService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected DriverService driverService;
106
Daniel Park81a61a12016-02-26 08:24:44 +0900107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected NetworkConfigService configService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected NetworkConfigRegistry configRegistry;
112
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected StorageService storageService;
Daniel Park81a61a12016-02-26 08:24:44 +0900115
Daniel Park23193902016-03-24 18:17:19 +0900116
sangho0c2a3da2016-02-16 13:39:07 +0900117 private ApplicationId appId;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900118 private ConsistentMap<Integer, String> tpPortNumMap; // Map<PortNum, allocated VM`s Mac & destionation Ip address>
119 private ConsistentMap<String, OpenstackFloatingIP> floatingIpMap; // Map<FloatingIp`s Id, FloatingIp object>
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900120 // Map<RouterInterface`s portId, Corresponded port`s network id>
121 private ConsistentMap<String, String> routerInterfaceMap;
sangho0c2a3da2016-02-16 13:39:07 +0900122 private static final String APP_ID = "org.onosproject.openstackrouting";
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900123 private static final String PORT_NAME = "portName";
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900124 private static final String PORTNAME_PREFIX_VM = "tap";
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900125 private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900126 private static final String FLOATING_IP_MAP_NAME = "openstackrouting-floatingip";
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900127 private static final String TP_PORT_MAP_NAME = "openstackrouting-tpportnum";
128 private static final String ROUTER_INTERFACE_MAP_NAME = "openstackrouting-routerinterface";
Daniel Park23193902016-03-24 18:17:19 +0900129 private static final String COLON = ":";
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900130 private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
131 private static final int TP_PORT_MINIMUM_NUM = 1024;
132 private static final int TP_PORT_MAXIMUM_NUM = 65535;
sangho48907542016-03-28 16:07:07 +0900133
Daniel Park81a61a12016-02-26 08:24:44 +0900134 private final ConfigFactory configFactory =
sangho48907542016-03-28 16:07:07 +0900135 new ConfigFactory(OpenstackSubjectFactories.USER_DEFINED_SUBJECT_FACTORY, OpenstackNetworkingConfig.class,
136 "config") {
Daniel Park81a61a12016-02-26 08:24:44 +0900137 @Override
sangho48907542016-03-28 16:07:07 +0900138 public OpenstackNetworkingConfig createConfig() {
139 return new OpenstackNetworkingConfig();
Daniel Park81a61a12016-02-26 08:24:44 +0900140 }
141 };
sangho48907542016-03-28 16:07:07 +0900142
Daniel Park81a61a12016-02-26 08:24:44 +0900143 private final NetworkConfigListener configListener = new InternalConfigListener();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900144
sangho48907542016-03-28 16:07:07 +0900145 private OpenstackNetworkingConfig config;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900146 private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER = KryoNamespace.newBuilder()
147 .register(KryoNamespaces.API)
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900148 .register(OpenstackFloatingIP.FloatingIpStatus.class)
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700149 .register(OpenstackFloatingIP.class);
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900150
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900151 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700152 .register(KryoNamespaces.API);
sangho0c2a3da2016-02-16 13:39:07 +0900153
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900154 private static final KryoNamespace.Builder ROUTER_INTERFACE_SERIALIZER = KryoNamespace.newBuilder()
HIGUCHI Yutac0f50452016-05-13 19:26:05 -0700155 .register(KryoNamespaces.API);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900156
sangho0c2a3da2016-02-16 13:39:07 +0900157 private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900158 private InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
sangho0c2a3da2016-02-16 13:39:07 +0900159 private ExecutorService l3EventExecutorService =
160 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
161 private ExecutorService icmpEventExecutorService =
162 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
Daniel Park81a61a12016-02-26 08:24:44 +0900163 private ExecutorService arpEventExecutorService =
164 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
165 private OpenstackIcmpHandler openstackIcmpHandler;
166 private OpenstackRoutingArpHandler openstackArpHandler;
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900167 private OpenstackRoutingRulePopulator rulePopulator;
Daniel Park23193902016-03-24 18:17:19 +0900168 private Map<DeviceId, Ip4Address> computeNodeMap;
sangho0c2a3da2016-02-16 13:39:07 +0900169
170 @Activate
171 protected void activate() {
172 appId = coreService.registerApplication(APP_ID);
173 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
Daniel Park81a61a12016-02-26 08:24:44 +0900174 configRegistry.registerConfigFactory(configFactory);
175 configService.addListener(configListener);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900176 deviceService.addListener(internalDeviceListener);
Daniel Park81a61a12016-02-26 08:24:44 +0900177
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900178 floatingIpMap = storageService.<String, OpenstackFloatingIP>consistentMapBuilder()
179 .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
180 .withName(FLOATING_IP_MAP_NAME)
181 .withApplicationId(appId)
182 .build();
183 tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
184 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
185 .withName(TP_PORT_MAP_NAME)
186 .withApplicationId(appId)
187 .build();
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900188 routerInterfaceMap = storageService.<String, String>consistentMapBuilder()
189 .withSerializer(Serializer.using(ROUTER_INTERFACE_SERIALIZER.build()))
190 .withName(ROUTER_INTERFACE_MAP_NAME)
191 .withApplicationId(appId)
192 .build();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900193
sangho48907542016-03-28 16:07:07 +0900194 readConfiguration();
195
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900196 log.info("started");
sangho0c2a3da2016-02-16 13:39:07 +0900197 }
198
199 @Deactivate
200 protected void deactivate() {
201 packetService.removeProcessor(internalPacketProcessor);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900202 deviceService.removeListener(internalDeviceListener);
Daniel Park81a61a12016-02-26 08:24:44 +0900203 l3EventExecutorService.shutdown();
204 icmpEventExecutorService.shutdown();
205 arpEventExecutorService.shutdown();
206
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900207 floatingIpMap.clear();
208 tpPortNumMap.clear();
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900209 routerInterfaceMap.clear();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900210
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900211 log.info("stopped");
sangho0c2a3da2016-02-16 13:39:07 +0900212 }
213
214
215 @Override
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900216 public void createFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
217 floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
sangho0c2a3da2016-02-16 13:39:07 +0900218 }
219
220 @Override
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900221 public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp) {
222 if (!floatingIpMap.containsKey(openstackFloatingIp.id())) {
223 log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIp.id());
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900224 return;
225 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900226 if (openstackFloatingIp.portId() == null || openstackFloatingIp.portId().equals("null")) {
227 OpenstackFloatingIP floatingIp = floatingIpMap.get(openstackFloatingIp.id()).value();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900228 OpenstackPortInfo portInfo = openstackSwitchingService.openstackPortInfo()
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900229 .get(PORTNAME_PREFIX_VM.concat(floatingIp.portId().substring(0, 11)));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900230 if (portInfo == null) {
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900231 log.warn("There`s no portInfo information about portId {}", floatingIp.portId());
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900232 return;
233 }
234 l3EventExecutorService.execute(
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900235 new OpenstackFloatingIPHandler(rulePopulator, floatingIp, false, portInfo));
236 floatingIpMap.replace(floatingIp.id(), openstackFloatingIp);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900237 } else {
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900238 floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900239 l3EventExecutorService.execute(
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900240 new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIp, true, null));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900241 }
sangho0c2a3da2016-02-16 13:39:07 +0900242 }
243
244 @Override
245 public void deleteFloatingIP(String id) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900246 floatingIpMap.remove(id);
sangho0c2a3da2016-02-16 13:39:07 +0900247 }
248
249 @Override
250 public void createRouter(OpenstackRouter openstackRouter) {
sangho0c2a3da2016-02-16 13:39:07 +0900251 }
252
253 @Override
254 public void updateRouter(OpenstackRouter openstackRouter) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900255 if (openstackRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
sanghoefbc0382016-04-06 13:35:38 +0900256 checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900257 } else {
258 unsetExternalConnection();
259 }
260 }
261
262 private void unsetExternalConnection() {
263 Collection<OpenstackRouter> internalRouters = getExternalRouter(false);
264 internalRouters.forEach(r ->
265 getOpenstackRouterInterface(r).forEach(i -> rulePopulator.removeExternalRules(i)));
266 }
267
268 private Collection<OpenstackRouter> getExternalRouter(boolean externalConnection) {
269 List<OpenstackRouter> routers;
270 if (externalConnection) {
271 routers = openstackService.routers()
272 .stream()
273 .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() > 0))
274 .collect(Collectors.toList());
275 } else {
276 routers = openstackService.routers()
277 .stream()
278 .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() == 0))
279 .collect(Collectors.toList());
280 }
281 return routers;
sangho0c2a3da2016-02-16 13:39:07 +0900282 }
283
284 @Override
285 public void deleteRouter(String id) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900286 //TODO : In now, there`s nothing to do for deleteRouter process. It is reserved.
sangho0c2a3da2016-02-16 13:39:07 +0900287 }
288
289 @Override
290 public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
sangho0c2a3da2016-02-16 13:39:07 +0900291 List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
292 routerInterfaces.add(routerInterface);
Daniel Park81a61a12016-02-26 08:24:44 +0900293 checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900294 setL3Connection(getOpenstackRouter(routerInterface.id()), null);
295 routerInterfaceMap.put(routerInterface.portId(), openstackService.port(routerInterface.portId()).networkId());
296 }
297
sanghod177f8f2016-06-29 21:52:23 +0900298 /**
299 * Set flow rules for traffic between two different subnets when more than one subnets
300 * connected to a router.
301 *
302 * @param openstackRouter OpenstackRouter Info
303 * @param openstackPort OpenstackPort Info
304 */
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900305 private void setL3Connection(OpenstackRouter openstackRouter, OpenstackPort openstackPort) {
306 Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(openstackRouter);
sanghod177f8f2016-06-29 21:52:23 +0900307
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900308 if (interfaceList.size() < 2) {
309 return;
310 }
311 if (openstackPort == null) {
312 interfaceList.forEach(i -> {
313 OpenstackPort interfacePort = openstackService.port(i.portId());
314 openstackService.ports()
315 .stream()
316 .filter(p -> p.networkId().equals(interfacePort.networkId())
317 && !p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
318 .forEach(p -> rulePopulator.populateL3Rules(p,
sanghod177f8f2016-06-29 21:52:23 +0900319 getL3ConnectionList(p.networkId(), interfaceList)));
320
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900321 });
322 } else {
323 rulePopulator.populateL3Rules(openstackPort, getL3ConnectionList(openstackPort.networkId(), interfaceList));
324 }
325
326 }
327
328 private List<OpenstackRouterInterface> getL3ConnectionList(String networkId,
329 Collection<OpenstackRouterInterface> interfaceList) {
330 List<OpenstackRouterInterface> targetList = Lists.newArrayList();
331 interfaceList.forEach(i -> {
332 OpenstackPort port = openstackService.port(i.portId());
333 if (!port.networkId().equals(networkId)) {
334 targetList.add(i);
335 }
336 });
337 return targetList;
sangho0c2a3da2016-02-16 13:39:07 +0900338 }
339
340 @Override
341 public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900342 OpenstackRouter router = openstackService.router(routerInterface.id());
343 Collection<OpenstackRouterInterface> interfaceList = getOpenstackRouterInterface(router);
344 if (interfaceList.size() == 1) {
345 List<OpenstackRouterInterface> newList = Lists.newArrayList();
346 newList.add(routerInterface);
347 interfaceList.forEach(i -> removeL3RulesForRouterInterface(i, router, newList));
348 }
349 removeL3RulesForRouterInterface(routerInterface, router, null);
sangho0c2a3da2016-02-16 13:39:07 +0900350 rulePopulator.removeExternalRules(routerInterface);
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900351 routerInterfaceMap.remove(routerInterface.portId());
352 }
353
354 private void removeL3RulesForRouterInterface(OpenstackRouterInterface routerInterface, OpenstackRouter router,
355 List<OpenstackRouterInterface> newList) {
356 openstackService.ports(routerInterfaceMap.get(routerInterface.portId()).value()).forEach(p -> {
357 Ip4Address vmIp = (Ip4Address) p.fixedIps().values().toArray()[0];
358 if (newList == null) {
359 rulePopulator.removeL3Rules(vmIp,
360 getL3ConnectionList(p.networkId(), getOpenstackRouterInterface(router)));
361 } else {
362 rulePopulator.removeL3Rules(vmIp, newList);
363 }
364 }
365 );
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900366 }
367
368 @Override
369 public void checkDisassociatedFloatingIp(String portId, OpenstackPortInfo portInfo) {
370 if (floatingIpMap.size() < 1) {
371 log.info("No information in FloatingIpMap");
372 return;
373 }
374 OpenstackFloatingIP floatingIp = associatedFloatingIps()
375 .stream()
376 .filter(fIp -> fIp.portId().equals(portId))
377 .findAny()
378 .orElse(null);
379 if (floatingIp != null && portInfo != null) {
380 l3EventExecutorService.execute(
381 new OpenstackFloatingIPHandler(rulePopulator, floatingIp, false, portInfo));
382 OpenstackFloatingIP.Builder fBuilder = new OpenstackFloatingIP.Builder()
383 .floatingIpAddress(floatingIp.floatingIpAddress())
384 .id(floatingIp.id())
385 .networkId(floatingIp.networkId())
386 .status(floatingIp.status())
387 .tenantId(floatingIp.tenantId());
388 floatingIpMap.replace(floatingIp.id(), fBuilder.build());
389 } else if (portInfo == null) {
390 log.warn("portInfo is null as timing issue between ovs port update event and openstack deletePort event");
391 }
392 }
393
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900394 @Override
395 public String networkIdForRouterInterface(String portId) {
396 return routerInterfaceMap.get(portId).value();
397 }
398
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900399 private Collection<OpenstackFloatingIP> associatedFloatingIps() {
400 List<OpenstackFloatingIP> fIps = Lists.newArrayList();
401 floatingIpMap.values()
402 .stream()
403 .filter(fIp -> fIp.value().portId() != null)
404 .forEach(fIp -> fIps.add(fIp.value()));
405 return fIps;
sangho0c2a3da2016-02-16 13:39:07 +0900406 }
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900407
408 private void reloadInitL3Rules() {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900409
sanghod177f8f2016-06-29 21:52:23 +0900410 l3EventExecutorService.execute(() ->
411 openstackService.ports()
412 .stream()
413 .forEach(p ->
414 {
415 if (p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) {
416 updateRouterInterface(portToRouterInterface(p));
417 } else {
418 Optional<Ip4Address> vmIp = p.fixedIps().values().stream().findAny();
419 if (vmIp.isPresent()) {
420 OpenstackFloatingIP floatingIP = getOpenstackFloatingIp(vmIp.get());
421 if (floatingIP != null) {
422 updateFloatingIP(floatingIP);
423 }
424 }
425 }
426 })
427 );
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900428 }
429
430 private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
431 OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
Daniel Park81a61a12016-02-26 08:24:44 +0900432 .id(checkNotNull(p.deviceId()))
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900433 .tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
434 .subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
Daniel Park81a61a12016-02-26 08:24:44 +0900435 .portId(checkNotNull(p.id()));
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900436
437 return osBuilder.build();
438 }
439
sangho0c2a3da2016-02-16 13:39:07 +0900440 private class InternalPacketProcessor implements PacketProcessor {
441
442 @Override
443 public void process(PacketContext context) {
444
445 if (context.isHandled()) {
446 return;
Daniel Park81a61a12016-02-26 08:24:44 +0900447 } else if (!context.inPacket().receivedFrom().deviceId().toString()
448 .equals(config.gatewayBridgeId())) {
449 return;
sangho0c2a3da2016-02-16 13:39:07 +0900450 }
451
452 InboundPacket pkt = context.inPacket();
453 Ethernet ethernet = pkt.parsed();
454
Daniel Park81a61a12016-02-26 08:24:44 +0900455 //TODO: Considers IPv6 later.
456 if (ethernet == null) {
457 return;
458 } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
sangho0c2a3da2016-02-16 13:39:07 +0900459 IPv4 iPacket = (IPv4) ethernet.getPayload();
sangho0c2a3da2016-02-16 13:39:07 +0900460 switch (iPacket.getProtocol()) {
461 case IPv4.PROTOCOL_ICMP:
Daniel Park81a61a12016-02-26 08:24:44 +0900462
Daniel Park23193902016-03-24 18:17:19 +0900463 icmpEventExecutorService.execute(() ->
Daniel Park81a61a12016-02-26 08:24:44 +0900464 openstackIcmpHandler.processIcmpPacket(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900465 break;
Hyunsun Moon0448ea02016-02-23 14:17:39 -0800466 case IPv4.PROTOCOL_UDP:
467 // don't process DHCP
468 UDP udpPacket = (UDP) iPacket.getPayload();
469 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
470 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
471 break;
472 }
sangho0c2a3da2016-02-16 13:39:07 +0900473 default:
474 int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
Daniel Park23193902016-03-24 18:17:19 +0900475 Optional<Port> port =
Daniel Park81a61a12016-02-26 08:24:44 +0900476 getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900477 if (port.isPresent()) {
Daniel Park23193902016-03-24 18:17:19 +0900478 OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(),
479 Ip4Address.valueOf(iPacket.getSourceAddress()));
480 l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context,
481 portNum, openstackPort, port.get(), config));
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900482 } else {
483 log.warn("There`s no external interface");
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900484 }
sangho0c2a3da2016-02-16 13:39:07 +0900485 break;
486 }
Daniel Park81a61a12016-02-26 08:24:44 +0900487 } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
Daniel Park23193902016-03-24 18:17:19 +0900488 arpEventExecutorService.execute(() ->
Daniel Park81a61a12016-02-26 08:24:44 +0900489 openstackArpHandler.processArpPacketFromRouter(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900490 }
491 }
492
493 private int getPortNum(MacAddress sourceMac, int destinationAddress) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900494 int portNum = findUnusedPortNum();
495 if (portNum == 0) {
496 clearPortNumMap();
497 portNum = findUnusedPortNum();
498 }
Daniel Park23193902016-03-24 18:17:19 +0900499 tpPortNumMap.put(portNum, sourceMac.toString().concat(COLON).concat(String.valueOf(destinationAddress)));
sangho0c2a3da2016-02-16 13:39:07 +0900500 return portNum;
501 }
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900502
503 private int findUnusedPortNum() {
504 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
505 if (!tpPortNumMap.containsKey(i)) {
506 return i;
507 }
508 }
509 return 0;
510 }
511
512 }
513
514 private void clearPortNumMap() {
515 tpPortNumMap.entrySet().forEach(e -> {
516 if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
517 tpPortNumMap.remove(e.getKey());
518 }
519 });
sangho0c2a3da2016-02-16 13:39:07 +0900520 }
521
Daniel Park23193902016-03-24 18:17:19 +0900522 private Optional<Port> getExternalPort(DeviceId deviceId, String interfaceName) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900523 return deviceService.getPorts(deviceId)
524 .stream()
525 .filter(p -> p.annotations().value(PORT_NAME).equals(interfaceName))
Daniel Park23193902016-03-24 18:17:19 +0900526 .findAny();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900527 }
528
sangho0c2a3da2016-02-16 13:39:07 +0900529 private void checkExternalConnection(OpenstackRouter router,
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900530 Collection<OpenstackRouterInterface> interfaces) {
sangho0c2a3da2016-02-16 13:39:07 +0900531 checkNotNull(router, "Router can not be null");
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900532 checkNotNull(interfaces, "routerInterfaces can not be null");
sangho0c2a3da2016-02-16 13:39:07 +0900533 Ip4Address externalIp = router.gatewayExternalInfo().externalFixedIps()
534 .values().stream().findFirst().orElse(null);
535 if ((externalIp == null) || (!router.gatewayExternalInfo().isEnablePnat())) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900536 log.debug("Not satisfied to set pnat configuration");
sangho0c2a3da2016-02-16 13:39:07 +0900537 return;
538 }
Daniel Park23193902016-03-24 18:17:19 +0900539 interfaces.forEach(this::initiateL3Rule);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900540 }
541
Daniel Park23193902016-03-24 18:17:19 +0900542 private Optional<OpenstackRouter> getRouterfromExternalIp(Ip4Address externalIp) {
543 return getExternalRouter(true)
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900544 .stream()
545 .filter(r -> r.gatewayExternalInfo()
546 .externalFixedIps()
547 .values()
548 .stream()
Daniel Park23193902016-03-24 18:17:19 +0900549 .findAny()
550 .get()
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900551 .equals(externalIp))
Daniel Park23193902016-03-24 18:17:19 +0900552 .findAny();
sangho0c2a3da2016-02-16 13:39:07 +0900553 }
554
Daniel Park23193902016-03-24 18:17:19 +0900555 private void initiateL3Rule(OpenstackRouterInterface routerInterface) {
sangho0c2a3da2016-02-16 13:39:07 +0900556 long vni = Long.parseLong(openstackService.network(openstackService
Daniel Park81a61a12016-02-26 08:24:44 +0900557 .port(routerInterface.portId()).networkId()).segmentId());
Daniel Park23193902016-03-24 18:17:19 +0900558 rulePopulator.populateExternalRules(vni);
sangho0c2a3da2016-02-16 13:39:07 +0900559 }
560
561 private Collection<OpenstackRouterInterface> getOpenstackRouterInterface(OpenstackRouter router) {
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900562 List<OpenstackRouterInterface> interfaces = Lists.newArrayList();
563 openstackService.ports()
564 .stream()
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900565 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
566 && p.deviceId().equals(router.id()))
567 .forEach(p -> interfaces.add(portToRouterInterface(p)));
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900568 return interfaces;
sangho0c2a3da2016-02-16 13:39:07 +0900569 }
570
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900571 private OpenstackRouter getOpenstackRouter(String id) {
sangho0c2a3da2016-02-16 13:39:07 +0900572 return openstackService.routers().stream().filter(r ->
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900573 r.id().equals(id)).iterator().next();
sangho0c2a3da2016-02-16 13:39:07 +0900574 }
575
576 private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900577 OpenstackPort openstackPort = openstackService.ports().stream()
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900578 .filter(p -> p.macAddress().equals(sourceMac)).iterator().next();
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900579 return openstackPort.fixedIps().values().stream().filter(ip ->
580 ip.equals(ip4Address)).count() > 0 ? openstackPort : null;
sangho0c2a3da2016-02-16 13:39:07 +0900581 }
582
sanghod177f8f2016-06-29 21:52:23 +0900583 private OpenstackFloatingIP getOpenstackFloatingIp(Ip4Address vmIp) {
584 Optional<OpenstackFloatingIP> floatingIp = floatingIpMap.asJavaMap().values().stream()
585 .filter(f -> f.portId() != null && f.fixedIpAddress().equals(vmIp))
586 .findAny();
587
588 if (floatingIp.isPresent()) {
589 return floatingIp.get();
590 }
591 log.debug("There is no floating IP information for VM IP {}", vmIp);
592
593 return null;
594 }
595
Daniel Park81a61a12016-02-26 08:24:44 +0900596 private void readConfiguration() {
sangho48907542016-03-28 16:07:07 +0900597 config = configService.getConfig("openstacknetworking", OpenstackNetworkingConfig.class);
Daniel Park81a61a12016-02-26 08:24:44 +0900598 if (config == null) {
599 log.error("No configuration found");
600 return;
601 }
602
603 checkNotNull(config.physicalRouterMac());
604 checkNotNull(config.gatewayBridgeId());
605 checkNotNull(config.gatewayExternalInterfaceMac());
606 checkNotNull(config.gatewayExternalInterfaceName());
607
sangho48907542016-03-28 16:07:07 +0900608 log.warn("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
Daniel Park81a61a12016-02-26 08:24:44 +0900609 config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
610
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900611 rulePopulator = new OpenstackRoutingRulePopulator(appId,
612 openstackService, flowObjectiveService, deviceService, driverService, config);
Daniel Park81a61a12016-02-26 08:24:44 +0900613
614 openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
615 openstackService, config, openstackSwitchingService);
616 openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, config);
617
618 openstackIcmpHandler.requestPacket(appId);
619 openstackArpHandler.requestPacket(appId);
sanghod177f8f2016-06-29 21:52:23 +0900620
621 openstackService.floatingIps().stream()
622 .forEach(f -> floatingIpMap.put(f.id(), f));
623
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900624 reloadInitL3Rules();
sangho48907542016-03-28 16:07:07 +0900625
Daniel Park81a61a12016-02-26 08:24:44 +0900626 log.info("OpenstackRouting configured");
627 }
628
629 private class InternalConfigListener implements NetworkConfigListener {
630
631 @Override
632 public void event(NetworkConfigEvent event) {
sangho48907542016-03-28 16:07:07 +0900633 if (!event.configClass().equals(OpenstackNetworkingConfig.class)) {
Daniel Park81a61a12016-02-26 08:24:44 +0900634 return;
635 }
636
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900637 if (event.type().equals(NetworkConfigEvent.Type.CONFIG_ADDED) ||
638 event.type().equals(NetworkConfigEvent.Type.CONFIG_UPDATED)) {
639 l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
Daniel Park81a61a12016-02-26 08:24:44 +0900640 }
641 }
642 }
Kyuhwi Choi5e65e2c2016-04-01 11:48:15 +0900643
644 private class InternalDeviceListener implements DeviceListener {
645
646 @Override
647 public void event(DeviceEvent deviceEvent) {
648 if (deviceEvent.type() == DeviceEvent.Type.PORT_UPDATED) {
649 Port port = deviceEvent.port();
650 OpenstackPortInfo portInfo = openstackSwitchingService.openstackPortInfo()
651 .get(port.annotations().value(PORT_NAME));
652 OpenstackPort openstackPort = openstackService.port(port);
653 OpenstackPort interfacePort = openstackService.ports()
654 .stream()
655 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)
656 && p.networkId().equals(openstackPort.networkId()))
657 .findAny()
658 .orElse(null);
659 if (portInfo == null && openstackPort == null) {
660 log.warn("As delete event timing issue between routing and switching, Can`t delete L3 rules");
661 return;
662 }
663 if ((port.isEnabled()) && (interfacePort != null)) {
664 OpenstackRouterInterface routerInterface = portToRouterInterface(interfacePort);
665 l3EventExecutorService.execute(() ->
666 setL3Connection(getOpenstackRouter(routerInterface.id()), openstackPort));
667 } else if (interfacePort != null) {
668 OpenstackRouterInterface routerInterface = portToRouterInterface(interfacePort);
669 l3EventExecutorService.execute(() -> rulePopulator.removeL3Rules(portInfo.ip(),
670 getL3ConnectionList(portInfo.networkId(),
671 getOpenstackRouterInterface(getOpenstackRouter(routerInterface.id())))));
672 }
673 }
674 }
675 }
676
sangho0c2a3da2016-02-16 13:39:07 +0900677}