blob: 9039ed26041d03f4062f711c0ccd8b802617b6be [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Jian Li26949762018-03-30 15:46:37 +09002 * Copyright 2016-present Open Networking Foundation
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 */
Hyunsun Moon44aac662017-02-18 02:07:01 +090016
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;
daniel parka792cf72017-04-14 16:25:35 +090026import org.onlab.packet.VlanId;
Jian Li5c09e212018-10-24 18:23:58 +090027import org.onosproject.cfg.ComponentConfigService;
28import org.onosproject.cfg.ConfigProperty;
Jian Li91be8cd2018-07-22 00:44:46 +090029import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.mastership.MastershipService;
daniel park796c2eb2018-03-22 17:01:51 +090035import org.onosproject.net.PortNumber;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.net.device.DeviceService;
sangho1aaa7882017-05-31 13:22:47 +090037import org.onosproject.net.driver.DriverService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090038import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090042import org.onosproject.openstacknetworking.api.InstancePort;
43import org.onosproject.openstacknetworking.api.InstancePortEvent;
44import org.onosproject.openstacknetworking.api.InstancePortListener;
45import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090046import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Jian Li91be8cd2018-07-22 00:44:46 +090047import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
48import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
Hyunsun Moon44aac662017-02-18 02:07:01 +090049import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090050import org.onosproject.openstacknode.api.OpenstackNode;
51import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.openstack4j.model.network.Network;
Frank Wangf9571662017-06-06 18:01:29 +080053import org.openstack4j.model.network.NetworkType;
54import org.openstack4j.model.network.Port;
Hyunsun Moon44aac662017-02-18 02:07:01 +090055import org.slf4j.Logger;
56
Jian Li91be8cd2018-07-22 00:44:46 +090057import java.util.Objects;
Jian Li5c09e212018-10-24 18:23:58 +090058import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090059import java.util.concurrent.ExecutorService;
60
61import static java.util.concurrent.Executors.newSingleThreadExecutor;
62import static org.onlab.util.Tools.groupedThreads;
sanghodc375372017-06-08 10:41:30 +090063import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
Jian Li5c09e212018-10-24 18:23:58 +090064import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
65import static org.onosproject.openstacknetworking.api.Constants.ARP_TABLE;
66import static org.onosproject.openstacknetworking.api.Constants.DHCP_TABLE;
Jian Li70a2c3f2018-04-13 17:26:31 +090067import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
sanghodc375372017-06-08 10:41:30 +090068import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
69import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Frank Wangf9571662017-06-06 18:01:29 +080070import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE;
Daniel Parkd1b14d32018-06-12 16:10:28 +090071import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_DOWNSTREAM_RULE;
72import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE;
73import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_JUMP_UPSTREAM_RULE;
74import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLAT_UPSTREAM_RULE;
sanghodc375372017-06-08 10:41:30 +090075import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
76import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
Jian Li960ae512018-07-03 22:50:56 +090077import static org.onosproject.openstacknetworking.api.Constants.STAT_FLAT_OUTBOUND_TABLE;
Jian Li70a2c3f2018-04-13 17:26:31 +090078import static org.onosproject.openstacknetworking.api.Constants.VTAG_TABLE;
Jian Li5c09e212018-10-24 18:23:58 +090079import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
Jian Liec5c32b2018-07-13 14:28:58 +090080import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
Jian Li26949762018-03-30 15:46:37 +090081import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
Hyunsun Moon0d457362017-06-27 17:19:41 +090082import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
Hyunsun Moon44aac662017-02-18 02:07:01 +090083import static org.slf4j.LoggerFactory.getLogger;
84
85
86/**
87 * Populates switching flow rules on OVS for the basic connectivity among the
88 * virtual instances in the same network.
89 */
90@Component(immediate = true)
91public final class OpenstackSwitchingHandler {
92
93 private final Logger log = getLogger(getClass());
94
Jian Li5c09e212018-10-24 18:23:58 +090095 private static final String ARP_MODE = "arpMode";
Jian Li71670d12018-03-02 21:31:07 +090096 private static final String ERR_SET_FLOWS_VNI = "Failed to set flows for %s: Failed to get VNI for %s";
Hyunsun Moon44aac662017-02-18 02:07:01 +090097
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +090099 protected CoreService coreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900102 protected MastershipService mastershipService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900105 protected DeviceService deviceService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li91be8cd2018-07-22 00:44:46 +0900108 protected DriverService driverService;
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected ClusterService clusterService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li5c09e212018-10-24 18:23:58 +0900114 protected ComponentConfigService configService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Li91be8cd2018-07-22 00:44:46 +0900117 protected LeadershipService leadershipService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900120 protected OpenstackFlowRuleService osFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900123 protected InstancePortService instancePortService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900126 protected OpenstackNetworkService osNetworkService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jian Liea1b9662018-03-02 18:07:32 +0900129 protected OpenstackNodeService osNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130
131 private final ExecutorService eventExecutor = newSingleThreadExecutor(
132 groupedThreads(this.getClass().getSimpleName(), "event-handler"));
133 private final InstancePortListener instancePortListener = new InternalInstancePortListener();
Jian Li91be8cd2018-07-22 00:44:46 +0900134 private final InternalOpenstackNetworkListener osNetworkListener =
135 new InternalOpenstackNetworkListener();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900136 private ApplicationId appId;
Jian Li91be8cd2018-07-22 00:44:46 +0900137 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900138
139 @Activate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800140 void activate() {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900141 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
Jian Li91be8cd2018-07-22 00:44:46 +0900142 localNodeId = clusterService.getLocalNode().id();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900143 instancePortService.addListener(instancePortListener);
Jian Li91be8cd2018-07-22 00:44:46 +0900144 osNetworkService.addListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900145
146 log.info("Started");
147 }
148
149 @Deactivate
Ray Milkey9c9cde42018-01-12 14:22:06 -0800150 void deactivate() {
Jian Li91be8cd2018-07-22 00:44:46 +0900151 osNetworkService.removeListener(osNetworkListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900152 instancePortService.removeListener(instancePortListener);
153 eventExecutor.shutdown();
154
155 log.info("Stopped");
156 }
157
Jian Liea1b9662018-03-02 18:07:32 +0900158 /**
159 * Configures L2 forwarding rules.
daniel park796c2eb2018-03-22 17:01:51 +0900160 * Currently, SONA supports Flat, VXLAN and VLAN modes.
Jian Liea1b9662018-03-02 18:07:32 +0900161 *
162 * @param instPort instance port object
163 * @param install install flag, add the rule if true, remove it otherwise
164 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900165 private void setNetworkRules(InstancePort instPort, boolean install) {
Jian Liea1b9662018-03-02 18:07:32 +0900166 NetworkType type = osNetworkService.network(instPort.networkId()).getNetworkType();
167 switch (type) {
daniel parka792cf72017-04-14 16:25:35 +0900168 case VXLAN:
Jian Li5c09e212018-10-24 18:23:58 +0900169 setTunnelTagIpFlowRules(instPort, install);
Jian Li9a921b42018-06-18 02:44:50 +0900170 setForwardingRulesForVxlan(instPort, install);
Jian Li5c09e212018-10-24 18:23:58 +0900171
172 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
173 setTunnelTagArpFlowRules(instPort, install);
174 }
175
daniel parka792cf72017-04-14 16:25:35 +0900176 break;
177 case VLAN:
Jian Li5c09e212018-10-24 18:23:58 +0900178 setVlanTagIpFlowRules(instPort, install);
daniel parka792cf72017-04-14 16:25:35 +0900179 setForwardingRulesForVlan(instPort, install);
Jian Li5c09e212018-10-24 18:23:58 +0900180
181 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
182 setVlanTagArpFlowRules(instPort, install);
183 }
184
daniel parka792cf72017-04-14 16:25:35 +0900185 break;
daniel park796c2eb2018-03-22 17:01:51 +0900186 case FLAT:
Jian Li70a2c3f2018-04-13 17:26:31 +0900187 setFlatJumpRules(instPort, install);
Jian Li9a921b42018-06-18 02:44:50 +0900188 setDownstreamRulesForFlat(instPort, install);
189 setUpstreamRulesForFlat(instPort, install);
daniel park796c2eb2018-03-22 17:01:51 +0900190 break;
daniel parka792cf72017-04-14 16:25:35 +0900191 default:
Jian Liea1b9662018-03-02 18:07:32 +0900192 log.warn("Unsupported network tunnel type {}", type.name());
daniel parka792cf72017-04-14 16:25:35 +0900193 break;
194 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900195 }
196
Jian Li24ec59f2018-05-23 19:01:25 +0900197 /**
198 * Removes virtual port.
199 *
200 * @param instPort instance port
201 */
202 private void removeVportRules(InstancePort instPort) {
203 NetworkType type = osNetworkService.network(instPort.networkId()).getNetworkType();
204
205 switch (type) {
206 case VXLAN:
Jian Li5c09e212018-10-24 18:23:58 +0900207 setTunnelTagIpFlowRules(instPort, false);
208
209 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
210 setTunnelTagArpFlowRules(instPort, false);
211 }
212
Jian Li24ec59f2018-05-23 19:01:25 +0900213 break;
214 case VLAN:
Jian Li5c09e212018-10-24 18:23:58 +0900215 setVlanTagIpFlowRules(instPort, false);
216
217 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
218 setVlanTagArpFlowRules(instPort, false);
219 }
220
Jian Li24ec59f2018-05-23 19:01:25 +0900221 break;
Jian Li9a921b42018-06-18 02:44:50 +0900222 case FLAT:
223 setFlatJumpRules(instPort, false);
224 setUpstreamRulesForFlat(instPort, false);
225 setDownstreamRulesForFlat(instPort, false);
Ray Milkeybcc53d32018-07-02 10:22:57 -0700226 break;
Jian Li24ec59f2018-05-23 19:01:25 +0900227 default:
Jian Li9a921b42018-06-18 02:44:50 +0900228 log.warn("Unsupported network type {}", type.name());
Jian Li24ec59f2018-05-23 19:01:25 +0900229 break;
230 }
231 }
232
Jian Li70a2c3f2018-04-13 17:26:31 +0900233 private void setFlatJumpRules(InstancePort port, boolean install) {
234 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
235 selector.matchInPort(port.portNumber());
236
237 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Jian Li960ae512018-07-03 22:50:56 +0900238 treatment.transition(STAT_FLAT_OUTBOUND_TABLE);
Jian Li70a2c3f2018-04-13 17:26:31 +0900239
240 osFlowRuleService.setRule(
241 appId,
242 port.deviceId(),
243 selector.build(),
244 treatment.build(),
Daniel Parkd1b14d32018-06-12 16:10:28 +0900245 PRIORITY_FLAT_JUMP_UPSTREAM_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900246 DHCP_TABLE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900247 install);
248
249 Network network = osNetworkService.network(port.networkId());
250
251 if (network == null) {
252 log.warn("The network does not exist");
253 return;
254 }
Jian Li70a2c3f2018-04-13 17:26:31 +0900255 PortNumber portNumber = osNodeService.node(port.deviceId())
256 .phyIntfPortNum(network.getProviderPhyNet());
257
258 if (portNumber == null) {
259 log.warn("The port number does not exist");
260 return;
261 }
262
263 selector = DefaultTrafficSelector.builder();
Daniel Parkd1b14d32018-06-12 16:10:28 +0900264 selector.matchInPort(portNumber)
265 .matchEthType(Ethernet.TYPE_IPV4)
266 .matchIPDst(port.ipAddress().toIpPrefix());
Jian Li70a2c3f2018-04-13 17:26:31 +0900267
268 osFlowRuleService.setRule(
269 appId,
270 port.deviceId(),
271 selector.build(),
272 treatment.build(),
Daniel Parkd1b14d32018-06-12 16:10:28 +0900273 PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900274 DHCP_TABLE,
Daniel Parkd1b14d32018-06-12 16:10:28 +0900275 install);
276
277 selector = DefaultTrafficSelector.builder();
278 selector.matchInPort(portNumber)
279 .matchEthType(Ethernet.TYPE_ARP)
280 .matchArpTpa(port.ipAddress().getIp4Address());
281
282 osFlowRuleService.setRule(
283 appId,
284 port.deviceId(),
285 selector.build(),
286 treatment.build(),
287 PRIORITY_FLAT_JUMP_DOWNSTREAM_RULE,
Jian Li5c09e212018-10-24 18:23:58 +0900288 DHCP_TABLE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900289 install);
290 }
291
Jian Li9a921b42018-06-18 02:44:50 +0900292 private void setDownstreamRulesForFlat(InstancePort instPort, boolean install) {
daniel park796c2eb2018-03-22 17:01:51 +0900293 TrafficSelector selector = DefaultTrafficSelector.builder()
294 .matchEthType(Ethernet.TYPE_IPV4)
295 .matchIPDst(instPort.ipAddress().toIpPrefix())
296 .build();
297 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
298 .setOutput(instPort.portNumber())
299 .build();
300
301 osFlowRuleService.setRule(
302 appId,
303 instPort.deviceId(),
304 selector,
305 treatment,
Daniel Parkd1b14d32018-06-12 16:10:28 +0900306 PRIORITY_FLAT_DOWNSTREAM_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900307 FLAT_TABLE,
daniel park796c2eb2018-03-22 17:01:51 +0900308 install);
309
310 selector = DefaultTrafficSelector.builder()
311 .matchEthType(Ethernet.TYPE_ARP)
312 .matchArpTpa(instPort.ipAddress().getIp4Address())
313 .build();
314
315 osFlowRuleService.setRule(
316 appId,
317 instPort.deviceId(),
318 selector,
319 treatment,
Daniel Parkd1b14d32018-06-12 16:10:28 +0900320 PRIORITY_FLAT_DOWNSTREAM_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900321 FLAT_TABLE,
daniel park796c2eb2018-03-22 17:01:51 +0900322 install);
323 }
324
Jian Li9a921b42018-06-18 02:44:50 +0900325 private void setUpstreamRulesForFlat(InstancePort instPort, boolean install) {
daniel park796c2eb2018-03-22 17:01:51 +0900326 TrafficSelector selector = DefaultTrafficSelector.builder()
327 .matchInPort(instPort.portNumber())
328 .build();
329
330 Network network = osNetworkService.network(instPort.networkId());
331
332 if (network == null) {
333 log.warn("The network does not exist");
334 return;
335 }
336
337 PortNumber portNumber = osNodeService.node(instPort.deviceId())
338 .phyIntfPortNum(network.getProviderPhyNet());
339
340 if (portNumber == null) {
341 log.warn("The port number does not exist");
342 return;
343 }
344
345 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
346 .setOutput(portNumber)
347 .build();
348
349 osFlowRuleService.setRule(
350 appId,
351 instPort.deviceId(),
352 selector,
353 treatment,
Daniel Parkd1b14d32018-06-12 16:10:28 +0900354 PRIORITY_FLAT_UPSTREAM_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900355 FLAT_TABLE,
daniel park796c2eb2018-03-22 17:01:51 +0900356 install);
357 }
358
Jian Liea1b9662018-03-02 18:07:32 +0900359 /**
360 * Configures the flow rules which are used for L2 packet switching.
361 * Note that these rules will be inserted in switching table (table 5).
362 *
363 * @param instPort instance port object
364 * @param install install flag, add the rule if true, remove it otherwise
365 */
Jian Li9a921b42018-06-18 02:44:50 +0900366 private void setForwardingRulesForVxlan(InstancePort instPort, boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900367 // switching rules for the instPorts in the same node
368 TrafficSelector selector = DefaultTrafficSelector.builder()
Jian Liea1b9662018-03-02 18:07:32 +0900369 // TODO: need to handle IPv6 in near future
Hyunsun Moon44aac662017-02-18 02:07:01 +0900370 .matchEthType(Ethernet.TYPE_IPV4)
371 .matchIPDst(instPort.ipAddress().toIpPrefix())
372 .matchTunnelId(getVni(instPort))
373 .build();
374
375 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Jian Liea1b9662018-03-02 18:07:32 +0900376 // TODO: this might not be necessary for the VMs located in the same subnet
Hyunsun Moon44aac662017-02-18 02:07:01 +0900377 .setEthDst(instPort.macAddress())
378 .setOutput(instPort.portNumber())
379 .build();
380
sanghodc375372017-06-08 10:41:30 +0900381 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900382 appId,
383 instPort.deviceId(),
384 selector,
385 treatment,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900386 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900387 FORWARDING_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900388 install);
389
390 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 OpenstackNode localNode = osNodeService.node(instPort.deviceId());
392 if (localNode == null) {
393 final String error = String.format("Cannot find openstack node for %s",
394 instPort.deviceId());
395 throw new IllegalStateException(error);
396 }
397 osNodeService.completeNodes(COMPUTE).stream()
398 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
399 .forEach(remoteNode -> {
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900400 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
401 .extension(buildExtension(
402 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900403 remoteNode.intgBridge(),
404 localNode.dataIp().getIp4Address()),
405 remoteNode.intgBridge())
406 .setOutput(remoteNode.tunnelPortNum())
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900407 .build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900408
sanghodc375372017-06-08 10:41:30 +0900409 osFlowRuleService.setRule(
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900410 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900411 remoteNode.intgBridge(),
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900412 selector,
413 treatmentToRemote,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900414 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900415 FORWARDING_TABLE,
Hyunsun Moonacde3f52017-02-23 17:57:35 +0900416 install);
417 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900418 }
419
Jian Liea1b9662018-03-02 18:07:32 +0900420 /**
421 * Configures the flow rules which are used for L2 VLAN packet switching.
422 * Note that these rules will be inserted in switching table (table 5).
423 *
424 * @param instPort instance port object
425 * @param install install flag, add the rule if true, remove it otherwise
426 */
daniel parka792cf72017-04-14 16:25:35 +0900427 private void setForwardingRulesForVlan(InstancePort instPort, boolean install) {
428 // switching rules for the instPorts in the same node
429 TrafficSelector selector = DefaultTrafficSelector.builder()
Jian Liea1b9662018-03-02 18:07:32 +0900430 // TODO: need to handle IPv6 in near future
daniel parka792cf72017-04-14 16:25:35 +0900431 .matchEthType(Ethernet.TYPE_IPV4)
432 .matchIPDst(instPort.ipAddress().toIpPrefix())
433 .matchVlanId(getVlanId(instPort))
434 .build();
435
436 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
437 .popVlan()
Jian Liea1b9662018-03-02 18:07:32 +0900438 // TODO: this might not be necessary for the VMs located in the same subnet
daniel parka792cf72017-04-14 16:25:35 +0900439 .setEthDst(instPort.macAddress())
440 .setOutput(instPort.portNumber())
441 .build();
442
sanghodc375372017-06-08 10:41:30 +0900443 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900444 appId,
445 instPort.deviceId(),
446 selector,
447 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900448 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900449 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900450 install);
451
452 // switching rules for the instPorts in the remote node
Hyunsun Moon0d457362017-06-27 17:19:41 +0900453 osNodeService.completeNodes(COMPUTE).stream()
454 .filter(remoteNode -> !remoteNode.intgBridge().equals(instPort.deviceId()) &&
455 remoteNode.vlanIntf() != null)
456 .forEach(remoteNode -> {
daniel parka792cf72017-04-14 16:25:35 +0900457 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
Daniel Parkc64b4c62018-05-09 18:13:39 +0900458 .setEthDst(instPort.macAddress())
459 .setOutput(remoteNode.vlanPortNum())
460 .build();
daniel parka792cf72017-04-14 16:25:35 +0900461
sanghodc375372017-06-08 10:41:30 +0900462 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900463 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900464 remoteNode.intgBridge(),
daniel parka792cf72017-04-14 16:25:35 +0900465 selector,
466 treatmentToRemote,
daniel parka792cf72017-04-14 16:25:35 +0900467 PRIORITY_SWITCHING_RULE,
sanghodc375372017-06-08 10:41:30 +0900468 FORWARDING_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900469 install);
470 });
daniel parka792cf72017-04-14 16:25:35 +0900471 }
472
Jian Li5c09e212018-10-24 18:23:58 +0900473 private void setTunnelTagArpFlowRules(InstancePort instPort, boolean install) {
474 setTunnelTagFlowRules(instPort, Ethernet.TYPE_ARP, install);
475 }
476
477 private void setTunnelTagIpFlowRules(InstancePort instPort, boolean install) {
478 setTunnelTagFlowRules(instPort, Ethernet.TYPE_IPV4, install);
479 }
480
Jian Liea1b9662018-03-02 18:07:32 +0900481 /**
482 * Configures the flow rule which is for using VXLAN to tag the packet
483 * based on the in_port number of a virtual instance.
Jian Li5c09e212018-10-24 18:23:58 +0900484 * Note that this rule will be inserted in vTag table.
Jian Liea1b9662018-03-02 18:07:32 +0900485 *
486 * @param instPort instance port object
487 * @param install install flag, add the rule if true, remove it otherwise
488 */
Jian Li5c09e212018-10-24 18:23:58 +0900489 private void setTunnelTagFlowRules(InstancePort instPort,
490 short ethType,
491 boolean install) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900492 TrafficSelector selector = DefaultTrafficSelector.builder()
Jian Li5c09e212018-10-24 18:23:58 +0900493 .matchEthType(ethType)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900494 .matchInPort(instPort.portNumber())
495 .build();
496
Jian Li28ec77f2018-10-31 07:07:25 +0900497 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
498 .setTunnelId(getVni(instPort));
sangho1aaa7882017-05-31 13:22:47 +0900499
sanghoe6457a32017-08-24 14:31:19 +0900500
Jian Li28ec77f2018-10-31 07:07:25 +0900501 if (ethType == Ethernet.TYPE_ARP) {
502 tBuilder.transition(ARP_TABLE);
503 } else if (ethType == Ethernet.TYPE_IPV4) {
504 tBuilder.transition(ACL_TABLE);
sanghoe6457a32017-08-24 14:31:19 +0900505 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900506
sanghodc375372017-06-08 10:41:30 +0900507 osFlowRuleService.setRule(
Hyunsun Moon44aac662017-02-18 02:07:01 +0900508 appId,
509 instPort.deviceId(),
510 selector,
Jian Li28ec77f2018-10-31 07:07:25 +0900511 tBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900512 PRIORITY_TUNNEL_TAG_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900513 VTAG_TABLE,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900514 install);
515 }
516
Jian Li5c09e212018-10-24 18:23:58 +0900517 private void setVlanTagIpFlowRules(InstancePort instPort, boolean install) {
518 setVlanTagFlowRules(instPort, Ethernet.TYPE_IPV4, install);
519 }
520
521 private void setVlanTagArpFlowRules(InstancePort instPort, boolean install) {
522 setVlanTagFlowRules(instPort, Ethernet.TYPE_ARP, install);
523 }
524
Jian Liea1b9662018-03-02 18:07:32 +0900525 /**
526 * Configures the flow rule which is for using VLAN to tag the packet
527 * based on the in_port number of a virtual instance.
Jian Li5c09e212018-10-24 18:23:58 +0900528 * Note that this rule will be inserted in vTag table.
Jian Liea1b9662018-03-02 18:07:32 +0900529 *
530 * @param instPort instance port object
531 * @param install install flag, add the rule if true, remove it otherwise
532 */
Jian Li5c09e212018-10-24 18:23:58 +0900533 private void setVlanTagFlowRules(InstancePort instPort,
534 short ethType,
535 boolean install) {
daniel parka792cf72017-04-14 16:25:35 +0900536 TrafficSelector selector = DefaultTrafficSelector.builder()
Jian Li5c09e212018-10-24 18:23:58 +0900537 .matchEthType(ethType)
daniel parka792cf72017-04-14 16:25:35 +0900538 .matchInPort(instPort.portNumber())
539 .build();
540
541 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
542 .pushVlan()
543 .setVlanId(getVlanId(instPort))
sanghodc375372017-06-08 10:41:30 +0900544 .transition(ACL_TABLE)
daniel parka792cf72017-04-14 16:25:35 +0900545 .build();
546
sanghodc375372017-06-08 10:41:30 +0900547 osFlowRuleService.setRule(
daniel parka792cf72017-04-14 16:25:35 +0900548 appId,
549 instPort.deviceId(),
550 selector,
551 treatment,
daniel parka792cf72017-04-14 16:25:35 +0900552 PRIORITY_TUNNEL_TAG_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900553 VTAG_TABLE,
daniel parka792cf72017-04-14 16:25:35 +0900554 install);
daniel parka792cf72017-04-14 16:25:35 +0900555 }
556
Jian Li91be8cd2018-07-22 00:44:46 +0900557 private void setNetworkBlockRules(Network network, boolean install) {
Frank Wangf9571662017-06-06 18:01:29 +0800558
Jian Li91be8cd2018-07-22 00:44:46 +0900559 NetworkType type = network.getNetworkType();
560
561 // TODO: we block a network traffic by referring to segment ID for now
562 // we might need to find a better way to block the traffic of a network
563 // in case the segment ID is overlapped in different types network (VXLAN, VLAN)
564 switch (type) {
565 case VXLAN:
566 setNetworkBlockRulesForVxlan(network.getProviderSegID(), install);
567 break;
568 case VLAN:
569 setNetworkBlockRulesForVlan(network.getProviderSegID(), install);
570 break;
571 case FLAT:
572 // TODO: need to find a way to block flat typed network
573 break;
574 default:
575 break;
Frank Wangf9571662017-06-06 18:01:29 +0800576 }
Jian Li91be8cd2018-07-22 00:44:46 +0900577 }
578
579 private void setNetworkBlockRulesForVxlan(String segmentId, boolean install) {
580 TrafficSelector selector = DefaultTrafficSelector.builder()
581 .matchTunnelId(Long.valueOf(segmentId))
582 .build();
Frank Wangf9571662017-06-06 18:01:29 +0800583
584 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
585 .drop()
586 .build();
587
Jian Li91be8cd2018-07-22 00:44:46 +0900588 osNodeService.completeNodes(COMPUTE)
Jian Liea1b9662018-03-02 18:07:32 +0900589 .forEach(osNode ->
Frank Wangf9571662017-06-06 18:01:29 +0800590 osFlowRuleService.setRule(
Jian Li91be8cd2018-07-22 00:44:46 +0900591 appId,
592 osNode.intgBridge(),
593 selector,
594 treatment,
595 PRIORITY_ADMIN_RULE,
596 ACL_TABLE,
597 install)
Jian Liea1b9662018-03-02 18:07:32 +0900598 );
Frank Wangf9571662017-06-06 18:01:29 +0800599 }
600
Jian Li91be8cd2018-07-22 00:44:46 +0900601 private void setNetworkBlockRulesForVlan(String segmentId, boolean install) {
Frank Wangf9571662017-06-06 18:01:29 +0800602 TrafficSelector selector = DefaultTrafficSelector.builder()
Jian Li91be8cd2018-07-22 00:44:46 +0900603 .matchTunnelId(Long.valueOf(segmentId))
604 .build();
605
606 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
607 .drop()
608 .build();
609
610 osNodeService.completeNodes(COMPUTE)
611 .forEach(osNode ->
612 osFlowRuleService.setRule(
613 appId,
614 osNode.intgBridge(),
615 selector,
616 treatment,
617 PRIORITY_ADMIN_RULE,
618 ACL_TABLE,
619 install)
620 );
621 }
622
623 private void setPortBlockRules(InstancePort instPort, boolean install) {
624 TrafficSelector selector = DefaultTrafficSelector.builder()
625 .matchInPort(instPort.portNumber())
Frank Wangf9571662017-06-06 18:01:29 +0800626 .build();
627
628 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
629 .drop()
630 .build();
631
632 osFlowRuleService.setRule(
633 appId,
Jian Li91be8cd2018-07-22 00:44:46 +0900634 instPort.deviceId(),
Frank Wangf9571662017-06-06 18:01:29 +0800635 selector,
636 treatment,
637 PRIORITY_ADMIN_RULE,
Jian Li70a2c3f2018-04-13 17:26:31 +0900638 VTAG_TABLE,
Frank Wangf9571662017-06-06 18:01:29 +0800639 install);
640 }
641
Jian Liea1b9662018-03-02 18:07:32 +0900642 /**
643 * Obtains the VLAN ID from the given instance port.
644 *
645 * @param instPort instance port object
646 * @return VLAN ID
647 */
daniel parka792cf72017-04-14 16:25:35 +0900648 private VlanId getVlanId(InstancePort instPort) {
649 Network osNet = osNetworkService.network(instPort.networkId());
650
651 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
Jian Li71670d12018-03-02 21:31:07 +0900652 final String error =
653 String.format(ERR_SET_FLOWS_VNI,
654 instPort, osNet == null ? "<none>" : osNet.getName());
daniel parka792cf72017-04-14 16:25:35 +0900655 throw new IllegalStateException(error);
656 }
657
658 return VlanId.vlanId(osNet.getProviderSegID());
659 }
660
Jian Liea1b9662018-03-02 18:07:32 +0900661 /**
662 * Obtains the VNI from the given instance port.
663 *
664 * @param instPort instance port object
665 * @return VXLAN Network Identifier (VNI)
666 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900667 private Long getVni(InstancePort instPort) {
668 Network osNet = osNetworkService.network(instPort.networkId());
669 if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
Jian Li71670d12018-03-02 21:31:07 +0900670 final String error =
671 String.format(ERR_SET_FLOWS_VNI,
672 instPort, osNet == null ? "<none>" : osNet.getName());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900673 throw new IllegalStateException(error);
674 }
675 return Long.valueOf(osNet.getProviderSegID());
676 }
677
Jian Li5c09e212018-10-24 18:23:58 +0900678 private String getArpMode() {
679 Set<ConfigProperty> properties = configService.getProperties(OpenstackSwitchingArpHandler.class.getName());
680 return getPropertyValue(properties, ARP_MODE);
681 }
682
Jian Liea1b9662018-03-02 18:07:32 +0900683 /**
684 * An internal instance port listener which listens the port events generated
685 * from VM. The corresponding L2 forwarding rules will be generated and
686 * inserted to integration bridge only if a new VM port is detected. If the
687 * existing detected VM port is removed due to VM purge, we will remove the
688 * corresponding L2 forwarding to as well for the sake of resource saving.
689 */
Hyunsun Moon44aac662017-02-18 02:07:01 +0900690 private class InternalInstancePortListener implements InstancePortListener {
691
692 @Override
693 public boolean isRelevant(InstancePortEvent event) {
694 InstancePort instPort = event.subject();
695 return mastershipService.isLocalMaster(instPort.deviceId());
696 }
697
698 @Override
699 public void event(InstancePortEvent event) {
700 InstancePort instPort = event.subject();
Jian Li91be8cd2018-07-22 00:44:46 +0900701 Port osPort = osNetworkService.port(instPort.portId());
Jian Li9a921b42018-06-18 02:44:50 +0900702
Hyunsun Moon44aac662017-02-18 02:07:01 +0900703 switch (event.type()) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900704 case OPENSTACK_INSTANCE_PORT_DETECTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900705 case OPENSTACK_INSTANCE_PORT_UPDATED:
Jian Li46b74002018-07-15 18:39:08 +0900706 case OPENSTACK_INSTANCE_RESTARTED:
Jian Liec5c32b2018-07-13 14:28:58 +0900707 log.info("SwitchingHandler: Instance port detected MAC:{} IP:{}",
Jian Li9a921b42018-06-18 02:44:50 +0900708 instPort.macAddress(),
709 instPort.ipAddress());
Jian Liec5c32b2018-07-13 14:28:58 +0900710
711 eventExecutor.execute(() -> instPortDetected(instPort));
Jian Li9a921b42018-06-18 02:44:50 +0900712
Jian Li91be8cd2018-07-22 00:44:46 +0900713 if (osPort != null) {
714 eventExecutor.execute(() ->
715 setPortBlockRules(instPort, !osPort.isAdminStateUp()));
716 }
717
Hyunsun Moon44aac662017-02-18 02:07:01 +0900718 break;
Jian Li46b74002018-07-15 18:39:08 +0900719 case OPENSTACK_INSTANCE_TERMINATED:
720 log.info("SwitchingHandler: Instance port terminated MAC:{} IP:{}",
721 instPort.macAddress(),
722 instPort.ipAddress());
723 eventExecutor.execute(() -> removeVportRules(instPort));
724
725 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900726 case OPENSTACK_INSTANCE_PORT_VANISHED:
Jian Liec5c32b2018-07-13 14:28:58 +0900727 log.info("SwitchingHandler: Instance port vanished MAC:{} IP:{}",
Jian Li9a921b42018-06-18 02:44:50 +0900728 instPort.macAddress(),
729 instPort.ipAddress());
Jian Liec5c32b2018-07-13 14:28:58 +0900730
731 eventExecutor.execute(() -> instPortRemoved(instPort));
Jian Li9a921b42018-06-18 02:44:50 +0900732
Jian Li91be8cd2018-07-22 00:44:46 +0900733 if (osPort != null) {
734 setPortBlockRules(instPort, false);
735 }
736
Hyunsun Moon44aac662017-02-18 02:07:01 +0900737 break;
Jian Liec5c32b2018-07-13 14:28:58 +0900738 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
739 log.info("SwitchingHandler: Migration started for MAC:{} IP:{}",
740 instPort.macAddress(),
Jian Li9a921b42018-06-18 02:44:50 +0900741 instPort.ipAddress());
Jian Liec5c32b2018-07-13 14:28:58 +0900742
743 eventExecutor.execute(() -> instPortDetected(instPort));
744
Jian Li91be8cd2018-07-22 00:44:46 +0900745 if (osPort != null) {
746 eventExecutor.execute(() ->
747 setPortBlockRules(instPort, !osPort.isAdminStateUp()));
748 }
749
Jian Liec5c32b2018-07-13 14:28:58 +0900750 break;
751 case OPENSTACK_INSTANCE_MIGRATION_ENDED:
752 log.info("SwitchingHandler: Migration finished for MAC:{} IP:{}",
753 instPort.macAddress(),
754 instPort.ipAddress());
755
756 InstancePort revisedInstPort = swapStaleLocation(instPort);
757 eventExecutor.execute(() -> removeVportRules(revisedInstPort));
758
Jian Li24ec59f2018-05-23 19:01:25 +0900759 break;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900760 default:
761 break;
762 }
763 }
764
765 private void instPortDetected(InstancePort instPort) {
766 setNetworkRules(instPort, true);
767 // TODO add something else if needed
768 }
769
770 private void instPortRemoved(InstancePort instPort) {
771 setNetworkRules(instPort, false);
772 // TODO add something else if needed
773 }
774 }
Jian Li91be8cd2018-07-22 00:44:46 +0900775
776 private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
777
778 @Override
779 public boolean isRelevant(OpenstackNetworkEvent event) {
780
781 // do not allow to proceed without leadership
782 NodeId leader = leadershipService.getLeader(appId.name());
783 return Objects.equals(localNodeId, leader);
784 }
785
786 @Override
787 public void event(OpenstackNetworkEvent event) {
788
789 if (event.subject() == null || event.port() == null) {
790 return;
791 }
792
793 boolean isNwAdminStateUp = event.subject().isAdminStateUp();
794 boolean isPortAdminStateUp = event.port().isAdminStateUp();
795
Jian Li32b03622018-11-06 17:54:24 +0900796 String portId = event.port().getId();
Jian Li91be8cd2018-07-22 00:44:46 +0900797
798 switch (event.type()) {
799 case OPENSTACK_NETWORK_CREATED:
800 case OPENSTACK_NETWORK_UPDATED:
801 eventExecutor.execute(() ->
802 setNetworkBlockRules(event.subject(), !isNwAdminStateUp));
803
804 break;
805 case OPENSTACK_NETWORK_REMOVED:
806 eventExecutor.execute(() ->
807 setNetworkBlockRules(event.subject(), false));
808 break;
809 case OPENSTACK_PORT_CREATED:
810 case OPENSTACK_PORT_UPDATED:
Jian Li32b03622018-11-06 17:54:24 +0900811 eventExecutor.execute(() -> {
812 InstancePort instPort = instancePortService.instancePort(portId);
813 if (instPort != null) {
814 setPortBlockRules(instPort, !isPortAdminStateUp);
815 }
816 });
Jian Li91be8cd2018-07-22 00:44:46 +0900817 break;
818 case OPENSTACK_PORT_REMOVED:
Jian Li32b03622018-11-06 17:54:24 +0900819 eventExecutor.execute(() -> {
820 InstancePort instPort = instancePortService.instancePort(portId);
821 if (instPort != null) {
822 setPortBlockRules(instPort, false);
823 }
824 });
Jian Li91be8cd2018-07-22 00:44:46 +0900825 break;
826 default:
827 break;
828 }
829 }
830 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900831}