blob: 46226604a195e0cab271ca9aa1142f58a572c797 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002* Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +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*/
16
17package org.onosproject.openstacknetworking.impl;
18
19import com.google.common.base.Strings;
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.onlab.packet.Ethernet;
Frank Wangf9571662017-06-06 18:01:29 +080026import org.onlab.packet.MacAddress;
daniel parka792cf72017-04-14 16:25:35 +090027import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.device.DeviceService;
sangho1aaa7882017-05-31 13:22:47 +090032import org.onosproject.net.driver.DriverService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090033import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
sangho1aaa7882017-05-31 13:22:47 +090037import org.onosproject.net.flow.instructions.ExtensionTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.openstacknetworking.api.InstancePort;
39import org.onosproject.openstacknetworking.api.InstancePortEvent;
40import org.onosproject.openstacknetworking.api.InstancePortListener;
41import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090042import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Frank Wangf9571662017-06-06 18:01:29 +080043import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
44import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090045import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090046import org.onosproject.openstacknode.api.OpenstackNode;
47import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090048import org.openstack4j.model.network.Network;
Frank Wangf9571662017-06-06 18:01:29 +080049import org.openstack4j.model.network.NetworkType;
50import org.openstack4j.model.network.Port;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.slf4j.Logger;
52
53import java.util.concurrent.ExecutorService;
54
55import static java.util.concurrent.Executors.newSingleThreadExecutor;
56import static org.onlab.util.Tools.groupedThreads;
Frank Wangf9571662017-06-06 18:01:29 +080057
sanghodc375372017-06-08 10:41:30 +090058import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
59import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
60import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Frank Wangf9571662017-06-06 18:01:29 +080061import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE;
sanghodc375372017-06-08 10:41:30 +090062import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
63import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
64import static org.onosproject.openstacknetworking.api.Constants.SRC_VNI_TABLE;
Frank Wangf9571662017-06-06 18:01:29 +080065import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
66import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
67import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
68import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_CREATED;
69import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_REMOVED;
70import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_UPDATED;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090072import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090073import static org.slf4j.LoggerFactory.getLogger;
74
75
76/**
77 * Populates switching flow rules on OVS for the basic connectivity among the
78 * virtual instances in the same network.
79 */
80@Component(immediate = true)
81public final class OpenstackSwitchingHandler {
82
83 private final Logger log = getLogger(getClass());
84
85 private static final String ERR_SET_FLOWS = "Failed to set flows for %s: ";
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected MastershipService mastershipService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DeviceService deviceService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sanghodc375372017-06-08 10:41:30 +090097 protected OpenstackFlowRuleService osFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090098
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected InstancePortService instancePortService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected OpenstackNetworkService osNetworkService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected OpenstackNodeService osNodeService;
107
sangho1aaa7882017-05-31 13:22:47 +0900108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DriverService driverService;
110
Hyunsun Moon44aac662017-02-18 02:07:01 +0900111 private final ExecutorService eventExecutor = newSingleThreadExecutor(
112 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
113 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Frank Wangf9571662017-06-06 18:01:29 +0800114 private final InternalOpenstackNetworkListener osNetworkListener =
115 new InternalOpenstackNetworkListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 private ApplicationId appId;
117
118 @Activate
119 protected void activate() {
120 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
121 instancePortService.addListener(instancePortListener);
Frank Wangf9571662017-06-06 18:01:29 +0800122 osNetworkService.addListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900123
124 log.info("Started");
125 }
126
127 @Deactivate
128 protected void deactivate() {
129 instancePortService.removeListener(instancePortListener);
Frank Wangf9571662017-06-06 18:01:29 +0800130 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900131 eventExecutor.shutdown();
132
133 log.info("Stopped");
134 }
135
136 private void setNetworkRules(InstancePort instPort, boolean install) {
daniel parka792cf72017-04-14 16:25:35 +0900137 switch (osNetworkService.network(instPort.networkId()).getNetworkType()) {
138 case VXLAN:
139 setTunnelTagFlowRules(instPort, install);
140 setForwardingRules(instPort, install);
141 break;
142 case VLAN:
143 setVlanTagFlowRules(instPort, install);
144 setForwardingRulesForVlan(instPort, install);
145 break;
146 default:
147 break;
148 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900149 }
150
151 private void setForwardingRules(InstancePort instPort, boolean install) {
152 // switching rules for the instPorts in the same node
153 TrafficSelector selector = DefaultTrafficSelector.builder()
154 .matchEthType(Ethernet.TYPE_IPV4)
155 .matchIPDst(instPort.ipAddress().toIpPrefix())
156 .matchTunnelId(getVni(instPort))
157 .build();
158
159 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
160 .setEthDst(instPort.macAddress())
161 .setOutput(instPort.portNumber())
162 .build();
163
sanghodc375372017-06-08 10:41:30 +0900164 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165 appId,
166 instPort.deviceId(),
167 selector,
168 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900170 FORWARDING_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900171 install);
172
173 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900174 OpenstackNode localNode = osNodeService.node(instPort.deviceId());
175 if (localNode == null) {
176 final String error = String.format("Cannot find openstack node for %s",
177 instPort.deviceId());
178 throw new IllegalStateException(error);
179 }
180 osNodeService.completeNodes(COMPUTE).stream()
181 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
182 .forEach(remoteNode -> {
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900183 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
184 .extension(buildExtension(
185 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900186 remoteNode.intgBridge(),
187 localNode.dataIp().getIp4Address()),
188 remoteNode.intgBridge())
189 .setOutput(remoteNode.tunnelPortNum())
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900190 .build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900191
sanghodc375372017-06-08 10:41:30 +0900192 osFlowRuleService.setRule(
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900193 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900194 remoteNode.intgBridge(),
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900195 selector,
196 treatmentToRemote,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900197 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900198 FORWARDING_TABLE,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900199 install);
200 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900201 }
202
daniel parka792cf72017-04-14 16:25:35 +0900203 private void setForwardingRulesForVlan(InstancePort instPort, boolean install) {
204 // switching rules for the instPorts in the same node
205 TrafficSelector selector = DefaultTrafficSelector.builder()
206 .matchEthType(Ethernet.TYPE_IPV4)
207 .matchIPDst(instPort.ipAddress().toIpPrefix())
208 .matchVlanId(getVlanId(instPort))
209 .build();
210
211 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
212 .popVlan()
213 .setEthDst(instPort.macAddress())
214 .setOutput(instPort.portNumber())
215 .build();
216
sanghodc375372017-06-08 10:41:30 +0900217 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900218 appId,
219 instPort.deviceId(),
220 selector,
221 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900222 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900223 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900224 install);
225
226 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900227 osNodeService.completeNodes(COMPUTE).stream()
228 .filter(remoteNode -> !remoteNode.intgBridge().equals(instPort.deviceId()) &&
229 remoteNode.vlanIntf() != null)
230 .forEach(remoteNode -> {
daniel parka792cf72017-04-14 16:25:35 +0900231 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
Hyunsun Moon0d457362017-06-27 17:19:41 +0900232 .setOutput(remoteNode.vlanPortNum())
daniel parka792cf72017-04-14 16:25:35 +0900233 .build();
234
sanghodc375372017-06-08 10:41:30 +0900235 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900236 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900237 remoteNode.intgBridge(),
daniel parka792cf72017-04-14 16:25:35 +0900238 selector,
239 treatmentToRemote,
daniel parka792cf72017-04-14 16:25:35 +0900240 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900241 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900242 install);
243 });
daniel parka792cf72017-04-14 16:25:35 +0900244 }
245
Hyunsun Moon44aac662017-02-18 02:07:01 +0900246 private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
247 TrafficSelector selector = DefaultTrafficSelector.builder()
248 .matchEthType(Ethernet.TYPE_IPV4)
249 .matchInPort(instPort.portNumber())
250 .build();
251
sangho1aaa7882017-05-31 13:22:47 +0900252 // XXX All egress traffic needs to go through connection tracking module, which might hurt its performance.
253 ExtensionTreatment ctTreatment =
254 RulePopulatorUtil.niciraConnTrackTreatmentBuilder(driverService, instPort.deviceId())
255 .commit(true).build();
256
Hyunsun Moon44aac662017-02-18 02:07:01 +0900257 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
258 .setTunnelId(getVni(instPort))
sanghodc375372017-06-08 10:41:30 +0900259 .transition(ACL_TABLE)
sangho1aaa7882017-05-31 13:22:47 +0900260 .extension(ctTreatment, instPort.deviceId())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900261 .build();
262
sanghodc375372017-06-08 10:41:30 +0900263 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900264 appId,
265 instPort.deviceId(),
266 selector,
267 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 PRIORITY_TUNNEL_TAG_RULE,
sanghodc375372017-06-08 10:41:30 +0900269 SRC_VNI_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900270 install);
271 }
272
daniel parka792cf72017-04-14 16:25:35 +0900273 private void setVlanTagFlowRules(InstancePort instPort, boolean install) {
274 TrafficSelector selector = DefaultTrafficSelector.builder()
275 .matchEthType(Ethernet.TYPE_IPV4)
276 .matchInPort(instPort.portNumber())
277 .build();
278
279 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
280 .pushVlan()
281 .setVlanId(getVlanId(instPort))
sanghodc375372017-06-08 10:41:30 +0900282 .transition(ACL_TABLE)
daniel parka792cf72017-04-14 16:25:35 +0900283 .build();
284
sanghodc375372017-06-08 10:41:30 +0900285 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900286 appId,
287 instPort.deviceId(),
288 selector,
289 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900290 PRIORITY_TUNNEL_TAG_RULE,
sanghodc375372017-06-08 10:41:30 +0900291 SRC_VNI_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900292 install);
293
294 }
295
Frank Wangf9571662017-06-06 18:01:29 +0800296 private void setNetworkAdminRules(Network network, boolean install) {
297 TrafficSelector selector;
298 if (network.getNetworkType() == NetworkType.VXLAN) {
299
300 selector = DefaultTrafficSelector.builder()
301 .matchTunnelId(Long.valueOf(network.getProviderSegID()))
302 .build();
303 } else {
304 selector = DefaultTrafficSelector.builder()
305 .matchVlanId(VlanId.vlanId(network.getProviderSegID()))
306 .build();
307 }
308
309 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
310 .drop()
311 .build();
312
313 osNodeService.completeNodes().stream()
314 .filter(osNode -> osNode.type() == COMPUTE)
315 .forEach(osNode -> {
316 osFlowRuleService.setRule(
317 appId,
318 osNode.intgBridge(),
319 selector,
320 treatment,
321 PRIORITY_ADMIN_RULE,
322 ACL_TABLE,
323 install);
324 });
325 }
326
327 private void setPortAdminRules(Port port, boolean install) {
328 InstancePort instancePort = instancePortService.instancePort(MacAddress.valueOf(port.getMacAddress()));
329 TrafficSelector selector = DefaultTrafficSelector.builder()
330 .matchInPort(instancePort.portNumber())
331 .build();
332
333 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
334 .drop()
335 .build();
336
337 osFlowRuleService.setRule(
338 appId,
339 instancePort.deviceId(),
340 selector,
341 treatment,
342 PRIORITY_ADMIN_RULE,
343 SRC_VNI_TABLE,
344 install);
345 }
346
daniel parka792cf72017-04-14 16:25:35 +0900347 private VlanId getVlanId(InstancePort instPort) {
348 Network osNet = osNetworkService.network(instPort.networkId());
349
350 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
351 final String error = String.format(
352 ERR_SET_FLOWS + "Failed to get VNI for %s",
353 instPort, osNet == null ? "<none>" : osNet.getName());
354 throw new IllegalStateException(error);
355 }
356
357 return VlanId.vlanId(osNet.getProviderSegID());
358 }
359
360
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 private Long getVni(InstancePort instPort) {
362 Network osNet = osNetworkService.network(instPort.networkId());
363 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
364 final String error = String.format(
365 ERR_SET_FLOWS + "Failed to get VNI for %s",
rohit8e5c8992017-03-29 19:15:30 +0530366 instPort, osNet == null ? "<none>" : osNet.getName());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900367 throw new IllegalStateException(error);
368 }
369 return Long.valueOf(osNet.getProviderSegID());
370 }
371
372 private class InternalInstancePortListener implements InstancePortListener {
373
374 @Override
375 public boolean isRelevant(InstancePortEvent event) {
376 InstancePort instPort = event.subject();
377 return mastershipService.isLocalMaster(instPort.deviceId());
378 }
379
380 @Override
381 public void event(InstancePortEvent event) {
382 InstancePort instPort = event.subject();
383 switch (event.type()) {
384 case OPENSTACK_INSTANCE_PORT_UPDATED:
385 case OPENSTACK_INSTANCE_PORT_DETECTED:
386 eventExecutor.execute(() -> {
387 log.info("Instance port detected MAC:{} IP:{}",
388 instPort.macAddress(),
389 instPort.ipAddress());
390 instPortDetected(event.subject());
391 });
392 break;
393 case OPENSTACK_INSTANCE_PORT_VANISHED:
394 eventExecutor.execute(() -> {
395 log.info("Instance port vanished MAC:{} IP:{}",
396 instPort.macAddress(),
397 instPort.ipAddress());
398 instPortRemoved(event.subject());
399 });
400 break;
401 default:
402 break;
403 }
404 }
405
406 private void instPortDetected(InstancePort instPort) {
407 setNetworkRules(instPort, true);
408 // TODO add something else if needed
409 }
410
411 private void instPortRemoved(InstancePort instPort) {
412 setNetworkRules(instPort, false);
413 // TODO add something else if needed
414 }
415 }
Frank Wangf9571662017-06-06 18:01:29 +0800416
417 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
418
419 @Override
420 public boolean isRelevant(OpenstackNetworkEvent event) {
421 return !(event.subject() == null && event.port() == null);
422 }
423
424 @Override
425 public void event(OpenstackNetworkEvent event) {
426
427 if ((event.type() == OPENSTACK_NETWORK_CREATED ||
428 event.type() == OPENSTACK_NETWORK_UPDATED) && !event.subject().isAdminStateUp()) {
429 setNetworkAdminRules(event.subject(), true);
430 } else if ((event.type() == OPENSTACK_NETWORK_UPDATED && event.subject().isAdminStateUp()) ||
431 (event.type() == OPENSTACK_NETWORK_REMOVED && !event.subject().isAdminStateUp())) {
432 setNetworkAdminRules(event.subject(), false);
433 }
434
435 if ((event.type() == OPENSTACK_PORT_CREATED ||
436 event.type() == OPENSTACK_PORT_UPDATED) && !event.port().isAdminStateUp()) {
437 setPortAdminRules(event.port(), true);
438 } else if ((event.type() == OPENSTACK_PORT_UPDATED && event.port().isAdminStateUp()) ||
439 (event.type() == OPENSTACK_PORT_REMOVED && !event.port().isAdminStateUp())) {
440 setPortAdminRules(event.port(), false);
441 }
442 }
443 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900444}