blob: 2ab19ac8d18ad3c605d3650e97bd29241f9fff9d [file] [log] [blame]
sangho0c2a3da2016-02-16 13:39:07 +09001/*
2 * Copyright 2016 Open Networking Laboratory
3 *
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;
19import com.google.common.collect.Maps;
20import 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;
29import org.onlab.packet.MacAddress;
Hyunsun Moon0448ea02016-02-23 14:17:39 -080030import org.onlab.packet.UDP;
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;
40import org.onosproject.net.config.basics.SubjectFactories;
sangho0c2a3da2016-02-16 13:39:07 +090041import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.driver.DriverService;
43import org.onosproject.net.flowobjective.FlowObjectiveService;
44import org.onosproject.net.packet.InboundPacket;
45import org.onosproject.net.packet.PacketContext;
46import org.onosproject.net.packet.PacketProcessor;
47import org.onosproject.net.packet.PacketService;
sangho93447f12016-02-24 00:33:22 +090048import org.onosproject.openstackinterface.OpenstackFloatingIP;
49import org.onosproject.openstackinterface.OpenstackInterfaceService;
50import org.onosproject.openstackinterface.OpenstackPort;
51import org.onosproject.openstackinterface.OpenstackRouter;
52import org.onosproject.openstackinterface.OpenstackRouterInterface;
sangho0c2a3da2016-02-16 13:39:07 +090053import org.onosproject.openstacknetworking.OpenstackRoutingService;
Daniel Park81a61a12016-02-26 08:24:44 +090054import org.onosproject.openstacknetworking.OpenstackSwitchingService;
sangho0c2a3da2016-02-16 13:39:07 +090055import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.Collection;
59import java.util.List;
60import java.util.Map;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.Executors;
63import java.util.stream.Collectors;
64
65import static com.google.common.base.Preconditions.checkNotNull;
66import static org.onlab.util.Tools.groupedThreads;
67
sangho0c2a3da2016-02-16 13:39:07 +090068@Component(immediate = true)
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090069@Service
sangho0c2a3da2016-02-16 13:39:07 +090070/**
71 * Populates flow rules about L3 functionality for VMs in Openstack.
72 */
73public class OpenstackRoutingManager implements OpenstackRoutingService {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090074
75 private final Logger log = LoggerFactory.getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090076
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected CoreService coreService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected PacketService packetService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected DeviceService deviceService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +090087 protected OpenstackInterfaceService openstackService;
sangho0c2a3da2016-02-16 13:39:07 +090088
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Daniel Park81a61a12016-02-26 08:24:44 +090090 protected OpenstackSwitchingService openstackSwitchingService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho0c2a3da2016-02-16 13:39:07 +090093 protected FlowObjectiveService flowObjectiveService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected DriverService driverService;
97
Daniel Park81a61a12016-02-26 08:24:44 +090098 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected NetworkConfigService configService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected NetworkConfigRegistry configRegistry;
103
104
105
sangho0c2a3da2016-02-16 13:39:07 +0900106 private ApplicationId appId;
107 private Map<String, OpenstackRouterInterface> routerInterfaceMap = Maps.newHashMap();
108 private Map<Integer, String> portNumMap = initPortNumMap();
109 private static final String APP_ID = "org.onosproject.openstackrouting";
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900110 private static final String PORT_NAME = "portName";
111 private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
Daniel Park81a61a12016-02-26 08:24:44 +0900112 private final ConfigFactory configFactory =
113 new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackRoutingConfig.class, "openstackrouting") {
114 @Override
115 public OpenstackRoutingConfig createConfig() {
116 return new OpenstackRoutingConfig();
117 }
118 };
119 private final NetworkConfigListener configListener = new InternalConfigListener();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900120
Daniel Park81a61a12016-02-26 08:24:44 +0900121 private OpenstackRoutingConfig config;
122 private static final int PNAT_PORT_NUM_START = 1024;
123 private static final int PNAT_PORT_NUM_END = 65535;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900124
sangho0c2a3da2016-02-16 13:39:07 +0900125 private Map<Integer, String> initPortNumMap() {
126 Map<Integer, String> map = Maps.newHashMap();
Daniel Park81a61a12016-02-26 08:24:44 +0900127 for (int i = PNAT_PORT_NUM_START; i < PNAT_PORT_NUM_END; i++) {
sangho0c2a3da2016-02-16 13:39:07 +0900128 map.put(i, "");
129 }
130 return map;
131 }
132
133 private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
134 private ExecutorService l3EventExecutorService =
135 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
136 private ExecutorService icmpEventExecutorService =
137 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
Daniel Park81a61a12016-02-26 08:24:44 +0900138 private ExecutorService arpEventExecutorService =
139 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
140 private OpenstackIcmpHandler openstackIcmpHandler;
141 private OpenstackRoutingArpHandler openstackArpHandler;
sangho0c2a3da2016-02-16 13:39:07 +0900142
143 @Activate
144 protected void activate() {
145 appId = coreService.registerApplication(APP_ID);
146 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
Daniel Park81a61a12016-02-26 08:24:44 +0900147 configRegistry.registerConfigFactory(configFactory);
148 configService.addListener(configListener);
149
150 readConfiguration();
151
sangho0c2a3da2016-02-16 13:39:07 +0900152 log.info("onos-openstackrouting started");
153 }
154
155 @Deactivate
156 protected void deactivate() {
157 packetService.removeProcessor(internalPacketProcessor);
Daniel Park81a61a12016-02-26 08:24:44 +0900158 l3EventExecutorService.shutdown();
159 icmpEventExecutorService.shutdown();
160 arpEventExecutorService.shutdown();
161
sangho0c2a3da2016-02-16 13:39:07 +0900162 log.info("onos-openstackrouting stopped");
163 }
164
165
166 @Override
167 public void createFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
168
169 }
170
171 @Override
172 public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
173
174 }
175
176 @Override
177 public void deleteFloatingIP(String id) {
178
179 }
180
181 @Override
182 public void createRouter(OpenstackRouter openstackRouter) {
183 checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
184 }
185
186 @Override
187 public void updateRouter(OpenstackRouter openstackRouter) {
188 checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
189 }
190
191 @Override
192 public void deleteRouter(String id) {
193 //TODO
194 }
195
196 @Override
197 public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
Daniel Park81a61a12016-02-26 08:24:44 +0900198 routerInterfaceMap.putIfAbsent(routerInterface.id(), routerInterface);
sangho0c2a3da2016-02-16 13:39:07 +0900199 List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
200 routerInterfaces.add(routerInterface);
Daniel Park81a61a12016-02-26 08:24:44 +0900201 checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
sangho0c2a3da2016-02-16 13:39:07 +0900202 }
203
204 @Override
205 public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
206 OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
Daniel Park81a61a12016-02-26 08:24:44 +0900207 openstackService, flowObjectiveService, deviceService, driverService, config);
sangho0c2a3da2016-02-16 13:39:07 +0900208 rulePopulator.removeExternalRules(routerInterface);
209 routerInterfaceMap.remove(routerInterface.portId());
210 }
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900211
212 private void reloadInitL3Rules() {
213 openstackService.ports()
214 .stream()
215 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
216 .forEach(p -> {
217 OpenstackRouterInterface routerInterface = portToRouterInterface(p);
218 updateRouterInterface(routerInterface);
219 });
220 }
221
222 private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
223 OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
Daniel Park81a61a12016-02-26 08:24:44 +0900224 .id(checkNotNull(p.deviceId()))
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900225 .tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
226 .subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
Daniel Park81a61a12016-02-26 08:24:44 +0900227 .portId(checkNotNull(p.id()));
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900228
229 return osBuilder.build();
230 }
231
sangho0c2a3da2016-02-16 13:39:07 +0900232 private class InternalPacketProcessor implements PacketProcessor {
233
234 @Override
235 public void process(PacketContext context) {
236
237 if (context.isHandled()) {
238 return;
Daniel Park81a61a12016-02-26 08:24:44 +0900239 } else if (!context.inPacket().receivedFrom().deviceId().toString()
240 .equals(config.gatewayBridgeId())) {
241 return;
sangho0c2a3da2016-02-16 13:39:07 +0900242 }
243
244 InboundPacket pkt = context.inPacket();
245 Ethernet ethernet = pkt.parsed();
246
Daniel Park81a61a12016-02-26 08:24:44 +0900247 //TODO: Considers IPv6 later.
248 if (ethernet == null) {
249 return;
250 } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
sangho0c2a3da2016-02-16 13:39:07 +0900251 IPv4 iPacket = (IPv4) ethernet.getPayload();
252 OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
Daniel Park81a61a12016-02-26 08:24:44 +0900253 openstackService, flowObjectiveService, deviceService, driverService, config);
sangho0c2a3da2016-02-16 13:39:07 +0900254 switch (iPacket.getProtocol()) {
255 case IPv4.PROTOCOL_ICMP:
Daniel Park81a61a12016-02-26 08:24:44 +0900256
257 icmpEventExecutorService.submit(() ->
258 openstackIcmpHandler.processIcmpPacket(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900259 break;
Hyunsun Moon0448ea02016-02-23 14:17:39 -0800260 case IPv4.PROTOCOL_UDP:
261 // don't process DHCP
262 UDP udpPacket = (UDP) iPacket.getPayload();
263 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
264 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
265 break;
266 }
sangho0c2a3da2016-02-16 13:39:07 +0900267 default:
268 int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
Daniel Park81a61a12016-02-26 08:24:44 +0900269 Port port =
270 getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900271 if (port == null) {
272 log.warn("There`s no external interface");
273 break;
274 }
sangho0c2a3da2016-02-16 13:39:07 +0900275 OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(),
276 Ip4Address.valueOf(iPacket.getSourceAddress()));
277 l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context,
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900278 portNum, openstackPort, port));
sangho0c2a3da2016-02-16 13:39:07 +0900279 break;
280 }
Daniel Park81a61a12016-02-26 08:24:44 +0900281 } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
282 arpEventExecutorService.submit(() ->
283 openstackArpHandler.processArpPacketFromRouter(context, ethernet));
sangho0c2a3da2016-02-16 13:39:07 +0900284 }
285 }
286
287 private int getPortNum(MacAddress sourceMac, int destinationAddress) {
288 int portNum = portNumMap.keySet().stream()
289 .filter(k -> portNumMap.get(k).equals("")).findFirst().orElse(0);
290 portNumMap.replace(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
291 return portNum;
292 }
293 }
294
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900295 private Port getExternalPort(DeviceId deviceId, String interfaceName) {
296 return deviceService.getPorts(deviceId)
297 .stream()
298 .filter(p -> p.annotations().value(PORT_NAME).equals(interfaceName))
299 .findAny()
300 .orElse(null);
301 }
302
sangho0c2a3da2016-02-16 13:39:07 +0900303 private void checkExternalConnection(OpenstackRouter router,
304 Collection<OpenstackRouterInterface> routerInterfaces) {
305 checkNotNull(router, "Router can not be null");
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900306 checkNotNull(routerInterfaces, "routerInterfaces can not be null");
sangho0c2a3da2016-02-16 13:39:07 +0900307 Ip4Address externalIp = router.gatewayExternalInfo().externalFixedIps()
308 .values().stream().findFirst().orElse(null);
309 if ((externalIp == null) || (!router.gatewayExternalInfo().isEnablePnat())) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900310 log.debug("Not satisfied to set pnat configuration");
sangho0c2a3da2016-02-16 13:39:07 +0900311 return;
312 }
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900313 routerInterfaces.forEach(routerInterface -> initiateL3Rule(router, routerInterface));
sangho0c2a3da2016-02-16 13:39:07 +0900314 }
315
316 private void initiateL3Rule(OpenstackRouter router, OpenstackRouterInterface routerInterface) {
317 long vni = Long.parseLong(openstackService.network(openstackService
Daniel Park81a61a12016-02-26 08:24:44 +0900318 .port(routerInterface.portId()).networkId()).segmentId());
sangho0c2a3da2016-02-16 13:39:07 +0900319 OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
Daniel Park81a61a12016-02-26 08:24:44 +0900320 openstackService, flowObjectiveService, deviceService, driverService, config);
sangho0c2a3da2016-02-16 13:39:07 +0900321 rulePopulator.populateExternalRules(vni, router, routerInterface);
322 }
323
324 private Collection<OpenstackRouterInterface> getOpenstackRouterInterface(OpenstackRouter router) {
325 return routerInterfaceMap.values().stream().filter(i -> i.id().equals(router.id()))
326 .collect(Collectors.toList());
327 }
328
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900329 private OpenstackRouter getOpenstackRouter(String id) {
sangho0c2a3da2016-02-16 13:39:07 +0900330 return openstackService.routers().stream().filter(r ->
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900331 r.id().equals(id)).findAny().orElse(null);
sangho0c2a3da2016-02-16 13:39:07 +0900332 }
333
334 private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900335 OpenstackPort openstackPort = openstackService.ports().stream()
sangho0c2a3da2016-02-16 13:39:07 +0900336 .filter(p -> p.macAddress().equals(sourceMac)).findFirst().orElse(null);
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900337 return checkNotNull(openstackPort.fixedIps().values().stream().findFirst().orElse(null))
sangho0c2a3da2016-02-16 13:39:07 +0900338 .equals(ip4Address) ? openstackPort : null;
339 }
340
Daniel Park81a61a12016-02-26 08:24:44 +0900341 private void readConfiguration() {
342 config = configService.getConfig(appId, OpenstackRoutingConfig.class);
343 if (config == null) {
344 log.error("No configuration found");
345 return;
346 }
347
348 checkNotNull(config.physicalRouterMac());
349 checkNotNull(config.gatewayBridgeId());
350 checkNotNull(config.gatewayExternalInterfaceMac());
351 checkNotNull(config.gatewayExternalInterfaceName());
352
353 log.debug("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
354 config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
355
356 reloadInitL3Rules();
357
358 openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
359 openstackService, config, openstackSwitchingService);
360 openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, config);
361
362 openstackIcmpHandler.requestPacket(appId);
363 openstackArpHandler.requestPacket(appId);
364
365 log.info("OpenstackRouting configured");
366 }
367
368 private class InternalConfigListener implements NetworkConfigListener {
369
370 @Override
371 public void event(NetworkConfigEvent event) {
372 if (!event.configClass().equals(OpenstackRoutingConfig.class)) {
373 return;
374 }
375
376 switch (event.type()) {
377 case CONFIG_ADDED:
378 case CONFIG_UPDATED:
379 l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
380 break;
381 default:
382 log.debug("Unsupported event type {}", event.type().toString());
383 break;
384 }
385 }
386 }
sangho0c2a3da2016-02-16 13:39:07 +0900387}