blob: 7d722ec53d42abce3d9bbe05fa58a95f440d5063 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
2 * Copyright 2016-present 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.impl;
17
18import com.google.common.base.Strings;
19import com.google.common.collect.ImmutableSet;
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;
26import org.onlab.packet.IPv4;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onosproject.cluster.ClusterService;
30import org.onosproject.cluster.LeadershipService;
31import org.onosproject.cluster.NodeId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.core.GroupId;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
41import org.onosproject.net.flowobjective.FlowObjectiveService;
42import org.onosproject.net.flowobjective.ForwardingObjective;
43import org.onosproject.openstacknetworking.api.Constants;
44import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
45import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
46import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
47import org.onosproject.openstacknetworking.api.OpenstackRouterService;
48import org.onosproject.openstacknode.OpenstackNode;
49import org.onosproject.openstacknode.OpenstackNodeEvent;
50import org.onosproject.openstacknode.OpenstackNodeListener;
51import org.onosproject.openstacknode.OpenstackNodeService;
52import org.onosproject.scalablegateway.api.ScalableGatewayService;
53import org.openstack4j.model.network.ExternalGateway;
54import org.openstack4j.model.network.Network;
55import org.openstack4j.model.network.Router;
56import org.openstack4j.model.network.RouterInterface;
57import org.openstack4j.model.network.Subnet;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
61import java.util.Objects;
62import java.util.Set;
63import java.util.concurrent.ExecutorService;
64import java.util.stream.Collectors;
65
66import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
67import static org.onlab.util.Tools.groupedThreads;
68import static org.onosproject.openstacknetworking.api.Constants.*;
69import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
70
71/**
72 * Handles OpenStack router events.
73 */
74@Component(immediate = true)
75public class OpenstackRoutingHandler {
76
77 private final Logger log = LoggerFactory.getLogger(getClass());
78
79 private static final String MSG_ENABLED = "Enabled ";
80 private static final String MSG_DISABLED = "Disabled ";
81 private static final String ERR_SET_FLOWS = "Failed to set flows for router %s:";
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected CoreService coreService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected LeadershipService leadershipService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 protected ClusterService clusterService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected FlowObjectiveService flowObjectiveService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected OpenstackNodeService osNodeService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected ScalableGatewayService gatewayService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected OpenstackNetworkService osNetworkService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected OpenstackRouterService osRouterService;
106
107 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
108 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
109 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
110 private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
111
112 private ApplicationId appId;
113 private NodeId localNodeId;
114
115 @Activate
116 protected void activate() {
117 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
118 localNodeId = clusterService.getLocalNode().id();
119 leadershipService.runForLeadership(appId.name());
120 osNodeService.addListener(osNodeListener);
121 osRouterService.addListener(osRouterListener);
122
123 log.info("Started");
124 }
125
126 @Deactivate
127 protected void deactivate() {
128 osRouterService.removeListener(osRouterListener);
129 osNodeService.removeListener(osNodeListener);
130 leadershipService.withdraw(appId.name());
131 eventExecutor.shutdown();
132
133 log.info("Stopped");
134 }
135
136 private void routerUpdated(Router osRouter) {
137 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
138 if (exGateway == null) {
139 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
140 setSourceNat(iface, false);
141 });
142 } else {
143 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
144 setSourceNat(iface, exGateway.isEnableSnat());
145 });
146 }
147 }
148
149 private void routerIfaceAdded(Router osRouter, RouterInterface osRouterIface) {
150 Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
151 if (osSubnet == null) {
152 final String error = String.format(
153 ERR_SET_FLOWS + "subnet %s does not exist",
154 osRouterIface.getId(),
155 osRouterIface.getSubnetId());
156 throw new IllegalStateException(error);
157 }
158
159 setInternalRoutes(osRouter, osSubnet, true);
160 setGatewayIcmp(osSubnet, true);
161 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
162 if (exGateway != null && exGateway.isEnableSnat()) {
163 setSourceNat(osRouterIface, true);
164 }
165
166 log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
167 }
168
169 private void routerIfaceRemoved(Router osRouter, RouterInterface osRouterIface) {
170 Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
171 if (osSubnet == null) {
172 final String error = String.format(
173 ERR_SET_FLOWS + "subnet %s does not exist",
174 osRouterIface.getId(),
175 osRouterIface.getSubnetId());
176 throw new IllegalStateException(error);
177 }
178
179 setInternalRoutes(osRouter, osSubnet, false);
180 setGatewayIcmp(osSubnet, false);
181 ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
182 if (exGateway != null && exGateway.isEnableSnat()) {
183 setSourceNat(osRouterIface, false);
184 }
185
186 log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
187 }
188
189 private void setSourceNat(RouterInterface routerIface, boolean install) {
190 Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
191 Network osNet = osNetworkService.network(osSubnet.getNetworkId());
192
193 osNodeService.completeNodes().stream()
194 .filter(osNode -> osNode.type() == COMPUTE)
195 .forEach(osNode -> {
196 setRulesToGateway(
197 osNode.intBridge(),
198 gatewayService.getGatewayGroupId(osNode.intBridge()),
199 Long.valueOf(osNet.getProviderSegID()),
200 IpPrefix.valueOf(osSubnet.getCidr()),
201 install);
202 });
203
204 // take the first outgoing packet to controller for source NAT
205 gatewayService.getGatewayDeviceIds()
206 .forEach(gwDeviceId -> setRulesToController(
207 gwDeviceId,
208 Long.valueOf(osNet.getProviderSegID()),
209 IpPrefix.valueOf(osSubnet.getCidr()),
210 install));
211
212 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
213 log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
214 }
215
216 private void setGatewayIcmp(Subnet osSubnet, boolean install) {
217 if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
218 // do nothing if no gateway is set
219 return;
220 }
221
222 // take ICMP request to a subnet gateway through gateway node group
223 Network network = osNetworkService.network(osSubnet.getNetworkId());
224 osNodeService.completeNodes().stream()
225 .filter(osNode -> osNode.type() == COMPUTE)
226 .forEach(osNode -> setRulesToGatewayWithDstIp(
227 osNode.intBridge(),
228 gatewayService.getGatewayGroupId(osNode.intBridge()),
229 Long.valueOf(network.getProviderSegID()),
230 IpAddress.valueOf(osSubnet.getGateway()),
231 install));
232
233 IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
234 gatewayService.getGatewayDeviceIds()
235 .forEach(gwDeviceId -> setGatewayIcmpRule(
236 gatewayIp,
237 gwDeviceId,
238 install
239 ));
240
241 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
242 log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
243 }
244
245 private void setInternalRoutes(Router osRouter, Subnet updatedSubnet, boolean install) {
246 Set<Subnet> routableSubnets = routableSubnets(osRouter, updatedSubnet.getId());
247 Long updatedVni = getVni(updatedSubnet);
248
249 // installs rule from/to my subnet intentionally to fix ICMP failure
250 // to my subnet gateway if no external gateway added to the router
251 osNodeService.completeNodes().stream()
252 .filter(osNode -> osNode.type() == COMPUTE)
253 .forEach(osNode -> {
254 setInternalRouterRules(
255 osNode.intBridge(),
256 updatedVni,
257 updatedVni,
258 IpPrefix.valueOf(updatedSubnet.getCidr()),
259 IpPrefix.valueOf(updatedSubnet.getCidr()),
260 install
261 );
262
263 routableSubnets.forEach(subnet -> {
264 setInternalRouterRules(
265 osNode.intBridge(),
266 updatedVni,
267 getVni(subnet),
268 IpPrefix.valueOf(updatedSubnet.getCidr()),
269 IpPrefix.valueOf(subnet.getCidr()),
270 install
271 );
272 setInternalRouterRules(
273 osNode.intBridge(),
274 getVni(subnet),
275 updatedVni,
276 IpPrefix.valueOf(subnet.getCidr()),
277 IpPrefix.valueOf(updatedSubnet.getCidr()),
278 install
279 );
280 });
281 });
282
283 final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
284 routableSubnets.forEach(subnet -> log.debug(
285 updateStr + "route between subnet:{} and subnet:{}",
286 subnet.getCidr(),
287 updatedSubnet.getCidr()));
288 }
289
290 private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
291 Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
292 .stream()
293 .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
294 .map(iface -> osNetworkService.subnet(iface.getSubnetId()))
295 .collect(Collectors.toSet());
296 return ImmutableSet.copyOf(osSubnets);
297 }
298
299 private Long getVni(Subnet osSubnet) {
300 return Long.parseLong(osNetworkService.network(
301 osSubnet.getNetworkId()).getProviderSegID());
302 }
303
304 private void setGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
305 TrafficSelector selector = DefaultTrafficSelector.builder()
306 .matchEthType(Ethernet.TYPE_IPV4)
307 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
308 .matchIPDst(gatewayIp.toIpPrefix())
309 .build();
310
311 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
312 .setOutput(PortNumber.CONTROLLER)
313 .build();
314
315 RulePopulatorUtil.setRule(
316 flowObjectiveService,
317 appId,
318 deviceId,
319 selector,
320 treatment,
321 ForwardingObjective.Flag.VERSATILE,
322 PRIORITY_ICMP_RULE,
323 install);
324 }
325
326 private void setInternalRouterRules(DeviceId deviceId, Long srcVni, Long dstVni,
327 IpPrefix srcSubnet, IpPrefix dstSubnet, boolean install) {
328 TrafficSelector selector = DefaultTrafficSelector.builder()
329 .matchEthType(Ethernet.TYPE_IPV4)
330 .matchTunnelId(srcVni)
331 .matchIPSrc(srcSubnet)
332 .matchIPDst(dstSubnet)
333 .build();
334
335 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
336 .setTunnelId(dstVni)
337 .build();
338
339 RulePopulatorUtil.setRule(
340 flowObjectiveService,
341 appId,
342 deviceId,
343 selector,
344 treatment,
345 ForwardingObjective.Flag.SPECIFIC,
346 PRIORITY_INTERNAL_ROUTING_RULE,
347 install);
348
349 selector = DefaultTrafficSelector.builder()
350 .matchEthType(Ethernet.TYPE_IPV4)
351 .matchTunnelId(dstVni)
352 .matchIPSrc(srcSubnet)
353 .matchIPDst(dstSubnet)
354 .build();
355
356 treatment = DefaultTrafficTreatment.builder()
357 .setTunnelId(dstVni)
358 .build();
359
360 RulePopulatorUtil.setRule(
361 flowObjectiveService,
362 appId,
363 deviceId,
364 selector,
365 treatment,
366 ForwardingObjective.Flag.SPECIFIC,
367 PRIORITY_INTERNAL_ROUTING_RULE,
368 install);
369 }
370
371 private void setRulesToGateway(DeviceId deviceId, GroupId groupId, Long vni,
372 IpPrefix srcSubnet, boolean install) {
373 TrafficSelector selector = DefaultTrafficSelector.builder()
374 .matchEthType(Ethernet.TYPE_IPV4)
375 .matchTunnelId(vni)
376 .matchIPSrc(srcSubnet)
377 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
378 .build();
379
380 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
381 .group(groupId)
382 .build();
383
384 RulePopulatorUtil.setRule(
385 flowObjectiveService,
386 appId,
387 deviceId,
388 selector,
389 treatment,
390 ForwardingObjective.Flag.SPECIFIC,
391 PRIORITY_EXTERNAL_ROUTING_RULE,
392 install);
393 }
394
395 private void setRulesToController(DeviceId deviceId, Long vni, IpPrefix srcSubnet, boolean install) {
396 TrafficSelector selector = DefaultTrafficSelector.builder()
397 .matchEthType(Ethernet.TYPE_IPV4)
398 .matchTunnelId(vni)
399 .matchIPSrc(srcSubnet)
400 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
401 .build();
402
403 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
404 .setOutput(PortNumber.CONTROLLER)
405 .build();
406
407 RulePopulatorUtil.setRule(
408 flowObjectiveService,
409 appId,
410 deviceId,
411 selector,
412 treatment,
413 ForwardingObjective.Flag.VERSATILE,
414 PRIORITY_EXTERNAL_ROUTING_RULE,
415 install);
416 }
417
418 private void setRulesToGatewayWithDstIp(DeviceId deviceId, GroupId groupId, Long vni,
419 IpAddress dstIp, boolean install) {
420 TrafficSelector selector = DefaultTrafficSelector.builder()
421 .matchEthType(Ethernet.TYPE_IPV4)
422 .matchTunnelId(vni)
423 .matchIPDst(dstIp.toIpPrefix())
424 .build();
425
426 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
427 .group(groupId)
428 .build();
429
430 RulePopulatorUtil.setRule(
431 flowObjectiveService,
432 appId,
433 deviceId,
434 selector,
435 treatment,
436 ForwardingObjective.Flag.SPECIFIC,
437 PRIORITY_SWITCHING_RULE,
438 install);
439 }
440
441 private class InternalRouterEventListener implements OpenstackRouterListener {
442
443 @Override
444 public boolean isRelevant(OpenstackRouterEvent event) {
445 // do not allow to proceed without leadership
446 NodeId leader = leadershipService.getLeader(appId.name());
447 return Objects.equals(localNodeId, leader);
448 }
449
450 // FIXME only one leader in the cluster should process
451 @Override
452 public void event(OpenstackRouterEvent event) {
453 switch (event.type()) {
454 case OPENSTACK_ROUTER_CREATED:
455 log.debug("Router(name:{}, ID:{}) is created",
456 event.subject().getName(),
457 event.subject().getId());
458 eventExecutor.execute(() -> routerUpdated(event.subject()));
459 break;
460 case OPENSTACK_ROUTER_UPDATED:
461 log.debug("Router(name:{}, ID:{}) is updated",
462 event.subject().getName(),
463 event.subject().getId());
464 eventExecutor.execute(() -> routerUpdated(event.subject()));
465 break;
466 case OPENSTACK_ROUTER_REMOVED:
467 log.debug("Router(name:{}, ID:{}) is removed",
468 event.subject().getName(),
469 event.subject().getId());
470 break;
471 case OPENSTACK_ROUTER_INTERFACE_ADDED:
472 log.debug("Router interface {} added to router {}",
473 event.routerIface().getPortId(),
474 event.routerIface().getId());
475 eventExecutor.execute(() -> routerIfaceAdded(
476 event.subject(),
477 event.routerIface()));
478 break;
479 case OPENSTACK_ROUTER_INTERFACE_UPDATED:
480 log.debug("Router interface {} on {} updated",
481 event.routerIface().getPortId(),
482 event.routerIface().getId());
483 break;
484 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
485 log.debug("Router interface {} removed from router {}",
486 event.routerIface().getPortId(),
487 event.routerIface().getId());
488 eventExecutor.execute(() -> routerIfaceRemoved(
489 event.subject(),
490 event.routerIface()));
491 break;
492 case OPENSTACK_ROUTER_GATEWAY_ADDED:
493 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
494 case OPENSTACK_FLOATING_IP_CREATED:
495 case OPENSTACK_FLOATING_IP_UPDATED:
496 case OPENSTACK_FLOATING_IP_REMOVED:
497 case OPENSTACK_FLOATING_IP_ASSOCIATED:
498 case OPENSTACK_FLOATING_IP_DISASSOCIATED:
499 default:
500 // do nothing for the other events
501 break;
502 }
503 }
504 }
505
506 private class InternalNodeEventListener implements OpenstackNodeListener {
507
508 @Override
509 public boolean isRelevant(OpenstackNodeEvent event) {
510 // do not allow to proceed without leadership
511 NodeId leader = leadershipService.getLeader(appId.name());
512 return Objects.equals(localNodeId, leader);
513 }
514
515 @Override
516 public void event(OpenstackNodeEvent event) {
517 OpenstackNode osNode = event.subject();
518
519 switch (event.type()) {
520 case COMPLETE:
521 case INCOMPLETE:
522 eventExecutor.execute(() -> {
523 log.info("COMPLETE node {} detected", osNode.hostname());
524 reconfigureRouters();
525 });
526 break;
527 case INIT:
528 case DEVICE_CREATED:
529 default:
530 break;
531 }
532 }
533
534 private void reconfigureRouters() {
535 osRouterService.routers().forEach(osRouter -> {
536 routerUpdated(osRouter);
537 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
538 routerIfaceAdded(osRouter, iface);
539 });
540 });
541 }
542 }
543}