blob: ee37a95e477280c4ded1c0d883e3d069c2ecdf5a [file] [log] [blame]
Lee Yongjae7c27bb42017-11-17 12:00:45 +09001/*
2 * Copyright 2017-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 */
16package org.onosproject.simplefabric;
17
18import com.google.common.collect.ImmutableList;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.osgi.service.component.annotations.Activate;
20import org.osgi.service.component.annotations.Component;
21import org.osgi.service.component.annotations.Deactivate;
22import org.osgi.service.component.annotations.Reference;
23import org.osgi.service.component.annotations.ReferenceCardinality;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090024import org.onlab.packet.EthType;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.ICMP;
27import org.onlab.packet.ICMP6;
28import org.onlab.packet.IPv4;
29import org.onlab.packet.IPv6;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
32import org.onlab.packet.Ip4Address;
33import org.onlab.packet.Ip4Prefix;
34import org.onlab.packet.Ip6Address;
35import org.onlab.packet.Ip6Prefix;
36import org.onlab.packet.MacAddress;
37import org.onlab.packet.VlanId;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Ray Milkey27cff8c2018-03-05 14:58:26 -080040import org.onosproject.net.FilteredConnectPoint;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090041import org.onosproject.net.intf.InterfaceService;
42import org.onosproject.net.ConnectPoint;
43import org.onosproject.net.EncapsulationType;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.device.DeviceService;
47import org.onosproject.net.flow.DefaultFlowRule;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.TrafficSelector;
51import org.onosproject.net.flow.TrafficTreatment;
52import org.onosproject.net.flow.FlowRule;
53import org.onosproject.net.flow.FlowRuleService;
54import org.onosproject.net.Host;
55import org.onosproject.net.host.HostService;
56import org.onosproject.net.intent.Constraint;
57import org.onosproject.net.intent.constraint.EncapsulationConstraint;
58import org.onosproject.net.intent.constraint.PartialFailureConstraint;
59import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
60import org.onosproject.net.intent.Intent;
61import org.onosproject.net.intent.IntentService;
62import org.onosproject.net.intent.Key;
63import org.onosproject.net.intent.MultiPointToSinglePointIntent;
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +090064import org.onosproject.net.intf.Interface;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090065import org.onosproject.net.link.LinkService;
66import org.onosproject.net.packet.DefaultOutboundPacket;
67import org.onosproject.net.packet.InboundPacket;
68import org.onosproject.net.packet.OutboundPacket;
69import org.onosproject.net.packet.PacketContext;
70import org.onosproject.net.packet.PacketPriority;
71import org.onosproject.net.packet.PacketProcessor;
72import org.onosproject.net.packet.PacketService;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090073import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
76import java.io.PrintStream;
77import java.nio.ByteBuffer;
78import java.util.ArrayList;
79import java.util.Collections;
80import java.util.Comparator;
81import java.util.HashSet;
82import java.util.List;
83import java.util.Set;
84
85
86/**
87 * SimpleFabricReactiveRouting handles L3 Reactive Routing.
88 */
89@Component(immediate = true, enabled = false)
90public class SimpleFabricReactiveRouting {
91
92 private final Logger log = LoggerFactory.getLogger(getClass());
93 private ApplicationId reactiveAppId;
94
Ray Milkeyd84f89b2018-08-17 14:54:17 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090096 protected CoreService coreService;
97
Ray Milkeyd84f89b2018-08-17 14:54:17 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +090099 protected PacketService packetService;
100
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900102 protected InterfaceService interfaceService;
103
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900105 protected DeviceService deviceService;
106
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900108 protected LinkService linkService;
109
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900111 protected HostService hostService;
112
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900114 protected IntentService intentService;
115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900117 protected FlowRuleService flowRuleService;
118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900120 protected SimpleFabricService simpleFabric;
121
122 private ImmutableList<Constraint> reactiveConstraints
123 = ImmutableList.of(new PartialFailureConstraint());
124 //= ImmutableList.of();
125 // NOTE: SHOULD NOT use HashedPathSelectionConstraint
126 // for unpredictable srcCp of Link appears as reactive packet traffic
127
128 private Set<FlowRule> interceptFlowRules = new HashSet<>();
129 private Set<Key> toBePurgedIntentKeys = new HashSet<>();
130 // NOTE: manage purged intents by key for intentService.getIntent() supports key only
131
132 private final InternalSimpleFabricListener simpleFabricListener = new InternalSimpleFabricListener();
133 private ReactiveRoutingProcessor processor = new ReactiveRoutingProcessor();
134
135 @Activate
136 public void activate() {
137 reactiveAppId = coreService.registerApplication(simpleFabric.REACTIVE_APP_ID);
138 log.info("simple fabric reactive routing starting with app id {}", reactiveAppId.toString());
139
140 // NOTE: may not clear at init for MIGHT generate pending_remove garbages
141 // use flush event from simple fabric cli command
142
143 if (simpleFabric.REACTIVE_HASHED_PATH_SELECTION) {
144 reactiveConstraints = ImmutableList.of(new PartialFailureConstraint(),
145 new HashedPathSelectionConstraint());
146 } else {
147 reactiveConstraints = ImmutableList.of(new PartialFailureConstraint());
148 }
149
150 processor = new ReactiveRoutingProcessor();
151 packetService.addProcessor(processor, PacketProcessor.director(2));
152 simpleFabric.addListener(simpleFabricListener);
153
154 registerIntercepts();
155 refreshIntercepts();
156
157 log.info("simple fabric reactive routing started");
158 }
159
160 @Deactivate
161 public void deactivate() {
162 log.info("simple fabric reactive routing stopping");
163
164 packetService.removeProcessor(processor);
165 simpleFabric.removeListener(simpleFabricListener);
166
167 withdrawIntercepts();
168
169 // NOTE: may not clear at init for MIGHT generate pending_remove garbages
170 // use flush event from simple fabric cli command
171
172 toBePurgedIntentKeys.clear();
173
174 flowRuleService.removeFlowRulesById(reactiveAppId);
175
176 processor = null;
177
178 log.info("simple fabric reactive routing stopped");
179 }
180
181 /**
182 * Request packet in via the PacketService.
183 */
184 private void registerIntercepts() {
185 // register default intercepts on packetService for broder routing intercepts
186
187 packetService.requestPackets(
188 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV4).build(),
189 PacketPriority.REACTIVE, reactiveAppId);
190
191 if (simpleFabric.ALLOW_IPV6) {
192 packetService.requestPackets(
193 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV6).build(),
194 PacketPriority.REACTIVE, reactiveAppId);
195 }
196
197 log.info("simple fabric reactive routing ip packet intercepts started");
198 }
199
200 /**
201 * Cancel request for packet in via PacketService.
202 */
203 private void withdrawIntercepts() {
204 // unregister default intercepts on packetService
205
206 packetService.cancelPackets(
207 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV4).build(),
208 PacketPriority.REACTIVE, reactiveAppId);
209
210 if (simpleFabric.ALLOW_IPV6) {
211 packetService.cancelPackets(
212 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV6).build(),
213 PacketPriority.REACTIVE, reactiveAppId);
214 }
215
216 log.info("simple fabric reactive routing ip packet intercepts stopped");
217 }
218
219 /**
220 * Refresh device flow rules for reative intercepts on local ipSubnets.
221 */
222 private void refreshIntercepts() {
223 Set<FlowRule> newInterceptFlowRules = new HashSet<>();
224 for (Device device : deviceService.getAvailableDevices()) {
225 for (IpSubnet subnet : simpleFabric.getIpSubnets()) {
226 newInterceptFlowRules.add(generateInterceptFlowRule(true, device.id(), subnet.ipPrefix()));
227 // check if this devices has the ipSubnet, then add ip broadcast flue rule
228 L2Network l2Network = simpleFabric.findL2Network(subnet.l2NetworkName());
229 if (l2Network != null && l2Network.contains(device.id())) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900230 newInterceptFlowRules.add(generateLocalSubnetIpBctFlowRule(device.id(), subnet.ipPrefix(),
231 l2Network));
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900232 }
233 // JUST FOR FLOW RULE TEST ONLY
234 //newInterceptFlowRules.add(generateTestFlowRule(device.id(), subnet.ipPrefix()));
235 }
236 for (Route route : simpleFabric.getBorderRoutes()) {
237 newInterceptFlowRules.add(generateInterceptFlowRule(false, device.id(), route.prefix()));
238 }
239 }
240
241 if (!newInterceptFlowRules.equals(interceptFlowRules)) {
242 // NOTE: DO NOT REMOVE INTERCEPT FLOW RULES FOR FAILED DEVICE FLOW UPDATE MIGHT BE BLOCKED
243 /*
244 interceptFlowRules.stream()
245 .filter(rule -> !newInterceptFlowRules.contains(rule))
246 .forEach(rule -> {
247 flowRuleService.removeFlowRules(rule);
248 log.info("simple fabric reactive routing remove intercept flow rule: {}", rule);
249 });
250 */
251 newInterceptFlowRules.stream()
252 .filter(rule -> !interceptFlowRules.contains(rule))
253 .forEach(rule -> {
254 flowRuleService.applyFlowRules(rule);
255 log.info("simple fabric reactive routing apply intercept flow rule: {}", rule);
256 });
257 interceptFlowRules = newInterceptFlowRules;
258 }
259 }
260
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900261 private FlowRule generateInterceptFlowRule(boolean isDstLocalSubnet, DeviceId deviceId, IpPrefix prefix) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900262 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
263 if (prefix.isIp4()) {
264 selector.matchEthType(Ethernet.TYPE_IPV4);
265 if (prefix.prefixLength() > 0) {
266 selector.matchIPDst(prefix);
267 }
268 } else {
269 selector.matchEthType(Ethernet.TYPE_IPV6);
270 if (prefix.prefixLength() > 0) {
271 selector.matchIPv6Dst(prefix);
272 }
273 }
274 FlowRule rule = DefaultFlowRule.builder()
275 .forDevice(deviceId)
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900276 .withPriority(reactivePriority(false, isDstLocalSubnet, prefix.prefixLength()))
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900277 .withSelector(selector.build())
278 .withTreatment(DefaultTrafficTreatment.builder().punt().build())
279 .fromApp(reactiveAppId)
280 .makePermanent()
281 .forTable(0).build();
282 return rule;
283 }
284
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900285 private FlowRule generateLocalSubnetIpBctFlowRule(DeviceId deviceId, IpPrefix prefix, L2Network l2Network) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900286 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
287 IpPrefix bctPrefix;
288 if (prefix.isIp4()) {
289 bctPrefix = Ip4Prefix.valueOf(prefix.getIp4Prefix().address().toInt() |
290 ~Ip4Address.makeMaskPrefix(prefix.prefixLength()).toInt(),
291 Ip4Address.BIT_LENGTH);
292 selector.matchEthType(Ethernet.TYPE_IPV4);
293 selector.matchIPDst(bctPrefix);
294 } else {
295 byte[] p = prefix.getIp6Prefix().address().toOctets();
296 byte[] m = Ip6Address.makeMaskPrefix(prefix.prefixLength()).toOctets();
297 for (int i = 0; i < p.length; i++) {
298 p[i] |= ~m[i];
299 }
300 bctPrefix = Ip6Prefix.valueOf(p, Ip6Address.BIT_LENGTH);
301 selector.matchEthType(Ethernet.TYPE_IPV6);
302 selector.matchIPv6Dst(bctPrefix);
303 }
304 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
305 Set<ConnectPoint> newEgressPoints = new HashSet<>();
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900306 for (Interface iface : l2Network.interfaces()) {
307 if (iface.connectPoint().deviceId().equals(deviceId)) {
308 treatment.setOutput(iface.connectPoint().port());
309 }
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900310 }
311 FlowRule rule = DefaultFlowRule.builder()
312 .forDevice(deviceId)
313 .withPriority(reactivePriority(true, true, bctPrefix.prefixLength()))
314 .withSelector(selector.build())
315 .withTreatment(treatment.build())
316 .fromApp(reactiveAppId)
317 .makePermanent()
318 .forTable(0).build();
319 return rule;
320 }
321
322 /**
323 * Refresh routes by examining network resource status.
324 */
325 private void refreshRouteIntents() {
326 for (Intent entry : intentService.getIntents()) {
327 if (!reactiveAppId.equals(entry.appId())) {
328 continue;
329 }
330
331 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
332
333 if (!intentService.isLocal(intent.key())) {
334 if (toBePurgedIntentKeys.contains(intent.key())) {
335 toBePurgedIntentKeys.remove(intent.key()); // clear non local intent
336 }
337 continue;
338 }
339
340 try {
341 switch (intentService.getIntentState(intent.key())) {
342 //case FAILED: // failed intent is not auto removed
343 case WITHDRAWN:
344 log.warn("intent found failed or withdrawn; "
345 + "remove and try to purge intent: key={}", intent.key());
346 // purge intents here without withdraw
347 intentService.purge(intentService.getIntent(intent.key()));
348 toBePurgedIntentKeys.add(intent.key());
349 continue;
350 default: // no action
351 break;
352 }
353 } catch (Exception e) {
354 log.warn("intent status lookup failed: error={}", e);
355 continue; // this intent seems invalid; no action
356 }
357
358 // dummy loop to break on remove cases
359 if (!deviceService.isAvailable(intent.egressPoint().deviceId())) {
360 log.info("refresh route intents; remove intent for no device: key={}", intent.key());
361 intentService.withdraw(intentService.getIntent(intent.key()));
362 toBePurgedIntentKeys.add(intent.key());
363 continue;
364 }
365 if (!(simpleFabric.findL2Network(intent.egressPoint(), VlanId.NONE) != null ||
366 (simpleFabric.REACTIVE_ALLOW_LINK_CP &&
367 !linkService.getEgressLinks(intent.egressPoint()).isEmpty()))) {
368 log.info("refresh route intents; remove intent for egress point not available: key={}", intent.key());
369 intentService.withdraw(intentService.getIntent(intent.key()));
370 toBePurgedIntentKeys.add(intent.key());
371 continue;
372 }
373
374 // MAY NEED TO CHECK: intent.egressPoint and intent.treatment's dstMac is valid against hosts
375 if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE && !simpleFabric.REACTIVE_ALLOW_LINK_CP) {
376 // single path intent only; no need to check ingress points
377 continue;
378 }
379
Ray Milkey27cff8c2018-03-05 14:58:26 -0800380 Set<FilteredConnectPoint> newIngressPoints = new HashSet<>();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900381 boolean ingressPointChanged = false;
Ray Milkey27cff8c2018-03-05 14:58:26 -0800382 for (FilteredConnectPoint cp : intent.filteredIngressPoints()) {
383 if (deviceService.isAvailable(cp.connectPoint().deviceId()) &&
384 (simpleFabric.findL2Network(cp.connectPoint(), VlanId.NONE) != null ||
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900385 (simpleFabric.REACTIVE_ALLOW_LINK_CP &&
Ray Milkey27cff8c2018-03-05 14:58:26 -0800386 !linkService.getIngressLinks(cp.connectPoint()).isEmpty()))) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900387 newIngressPoints.add(cp);
388 } else {
389 log.info("refresh route ingress cp of "
390 + "not in 2Networks nor links: {}", cp);
391 ingressPointChanged = true;
392 }
393 }
394 if (newIngressPoints.isEmpty()) {
395 log.info("refresh route intents; "
396 + "remove intent for no ingress nor egress point available: key={}", intent.key());
397 intentService.withdraw(intentService.getIntent(intent.key()));
398 toBePurgedIntentKeys.add(intent.key());
399 continue;
400 }
401 // update ingress points
402 if (ingressPointChanged) {
403 MultiPointToSinglePointIntent updatedIntent =
404 MultiPointToSinglePointIntent.builder()
405 .appId(reactiveAppId)
406 .key(intent.key())
407 .selector(intent.selector())
408 .treatment(intent.treatment())
Ray Milkey27cff8c2018-03-05 14:58:26 -0800409 .filteredIngressPoints(newIngressPoints)
410 .filteredEgressPoint(intent.filteredEgressPoint())
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900411 .priority(intent.priority())
412 .constraints(intent.constraints())
413 .build();
414 log.info("refresh route update intent: key={} updatedIntent={}",
415 intent.key(), updatedIntent);
416 toBePurgedIntentKeys.remove(intent.key()); // may remove from old purged entry
417 intentService.submit(updatedIntent);
418 }
419 }
420 }
421
422 private void checkIntentsPurge() {
423 // check intents to be purge
424 if (!toBePurgedIntentKeys.isEmpty()) {
425 Set<Key> removeKeys = new HashSet<>();
426 for (Key key : toBePurgedIntentKeys) {
427 if (!intentService.isLocal(key)) {
428 removeKeys.add(key);
429 continue;
430 }
431 Intent intentToPurge = intentService.getIntent(key);
432 if (intentToPurge == null) {
433 log.info("purged intent: key={}", key);
434 removeKeys.add(key);
435 } else {
436 switch (intentService.getIntentState(key)) {
437 // case FAILED: // not auto removed
438 case WITHDRAWN:
439 log.info("try to purge intent: key={}", key);
440 intentService.purge(intentToPurge);
441 break;
442 case INSTALL_REQ:
443 case INSTALLED:
444 case INSTALLING:
445 case RECOMPILING:
446 case COMPILING:
447 log.warn("not to purge for active intent: key={}", key);
448 removeKeys.add(key);
449 break;
450 case WITHDRAW_REQ:
451 case WITHDRAWING:
452 case PURGE_REQ:
453 case CORRUPT:
454 default:
455 // no action
456 break;
457 }
458 }
459 }
460 toBePurgedIntentKeys.removeAll(removeKeys);
461 }
462 }
463
464 public void withdrawAllReactiveIntents() {
465 // check all intents of this app
466 // NOTE: cli calls are handling within the cli called node only; so should not user inents.isLocal()
467 Set<Intent> myIntents = new HashSet<>();
468 for (Intent intent : intentService.getIntents()) {
469 if (reactiveAppId.equals(intent.appId())) {
470 myIntents.add(intent);
471 }
472 }
473 // withdraw all my intents
474 for (Intent intent : myIntents) {
475 switch (intentService.getIntentState(intent.key())) {
476 case FAILED:
477 intentService.purge(intent);
478 toBePurgedIntentKeys.add(intent.key());
479 break;
480 case WITHDRAWN:
481 intentService.purge(intent);
482 toBePurgedIntentKeys.add(intent.key());
483 break;
484 case INSTALL_REQ:
485 case INSTALLED:
486 case INSTALLING:
487 case RECOMPILING:
488 case COMPILING:
489 intentService.withdraw(intent);
490 toBePurgedIntentKeys.add(intent.key());
491 break;
492 case WITHDRAW_REQ:
493 case WITHDRAWING:
494 toBePurgedIntentKeys.add(intent.key());
495 break;
496 case PURGE_REQ:
497 case CORRUPT:
498 default:
499 // no action
500 break;
501 }
502 }
503 }
504
505 /**
506 * Reactive Packet Handling.
507 */
508 private class ReactiveRoutingProcessor implements PacketProcessor {
509 @Override
510 public void process(PacketContext context) {
511 InboundPacket pkt = context.inPacket();
512 Ethernet ethPkt = pkt.parsed();
513 if (ethPkt == null) {
514 return;
515 }
516 ConnectPoint srcCp = pkt.receivedFrom();
517 IpAddress srcIp;
518 IpAddress dstIp;
519 byte ipProto = 0; /* 0 or tcp, udp */
520
521 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
522 case IPV4:
523 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
524 srcIp = IpAddress.valueOf(ipv4Packet.getSourceAddress());
525 dstIp = IpAddress.valueOf(ipv4Packet.getDestinationAddress());
526 ipProto = ipv4Packet.getProtocol();
527 break;
528 case IPV6:
529 IPv6 ipv6Packet = (IPv6) ethPkt.getPayload();
530 srcIp = IpAddress.valueOf(IpAddress.Version.INET6, ipv6Packet.getSourceAddress());
531 dstIp = IpAddress.valueOf(IpAddress.Version.INET6, ipv6Packet.getDestinationAddress());
532 ipProto = ipv6Packet.getNextHeader();
533 break;
534 default:
535 return; // ignore unknow ether type packets
536 }
537 if (ipProto != 6 && ipProto != 17) {
538 ipProto = 0; /* handle special for TCP and UDP only */
539 }
540
541 if (!checkVirtualGatewayIpPacket(pkt, srcIp, dstIp)) {
542 ipPacketReactiveProcessor(context, ethPkt, srcCp, srcIp, dstIp, ipProto);
543 // TODO: add ReactiveRouting for dstIp to srcIp with discovered egressCp as srcCp
544 }
545 }
546 }
547
548 /**
549 * handle Packet with dstIp=virtualGatewayIpAddresses.
550 * returns true(handled) or false(not for virtual gateway)
551 */
552 private boolean checkVirtualGatewayIpPacket(InboundPacket pkt, IpAddress srcIp, IpAddress dstIp) {
553 Ethernet ethPkt = pkt.parsed(); // assume valid
554
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900555 MacAddress mac = simpleFabric.findVMacForIp(dstIp);
556 if (mac == null || !simpleFabric.isVMac(ethPkt.getDestinationMAC())) {
557 /* Destination MAC should be any of virtual gateway macs */
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900558 return false;
559 } else if (dstIp.isIp4()) {
560 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
561 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_ICMP) {
562 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
563
564 if (icmpPacket.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
565 log.info("IPV4 ICMP ECHO request to virtual gateway: "
566 + "srcIp={} dstIp={} proto={}", srcIp, dstIp, ipv4Packet.getProtocol());
567 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
568 .setOutput(pkt.receivedFrom().port()).build();
569 OutboundPacket packet =
570 new DefaultOutboundPacket(pkt.receivedFrom().deviceId(), treatment,
571 ByteBuffer.wrap(icmpPacket.buildIcmpReply(pkt.parsed()).serialize()));
572 packetService.emit(packet);
573 return true;
574 }
575 }
576 log.warn("IPV4 packet to virtual gateway dropped: "
577 + "srcIp={} dstIp={} proto={}", srcIp, dstIp, ipv4Packet.getProtocol());
578 return true;
579
580 } else if (dstIp.isIp6()) {
581 // TODO: not tested yet (2017-07-20)
582 IPv6 ipv6Packet = (IPv6) ethPkt.getPayload();
583 if (ipv6Packet.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
584 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
585
586 if (icmp6Packet.getIcmpType() == ICMP6.ECHO_REQUEST) {
587 log.info("IPV6 ICMP6 ECHO request to virtual gateway: srcIp={} dstIp={} nextHeader={}",
588 srcIp, dstIp, ipv6Packet.getNextHeader());
589 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
590 .setOutput(pkt.receivedFrom().port()).build();
591 OutboundPacket packet =
592 new DefaultOutboundPacket(pkt.receivedFrom().deviceId(), treatment,
593 ByteBuffer.wrap(icmp6Packet.buildIcmp6Reply(pkt.parsed()).serialize()));
594 packetService.emit(packet);
595 return true;
596 }
597 }
598 log.warn("IPV6 packet to virtual gateway dropped: srcIp={} dstIp={} nextHeader={}",
599 srcIp, dstIp, ipv6Packet.getNextHeader());
600 return true;
601
602 }
603 return false; // unknown traffic
604 }
605
606 /**
607 * Routes packet reactively.
608 */
609 private void ipPacketReactiveProcessor(PacketContext context, Ethernet ethPkt, ConnectPoint srcCp,
610 IpAddress srcIp, IpAddress dstIp, byte ipProto) {
611 /* check reactive handling and forward packet */
612 log.trace("ip packet: srcCp={} srcIp={} dstIp={} ipProto={}",
613 srcCp, srcIp, dstIp, ipProto);
614
615 EncapsulationType encap = EncapsulationType.NONE;
616
617 // prefix and nextHop for local Subnet
618 IpPrefix srcPrefix = srcIp.toIpPrefix();
619 IpPrefix dstPrefix = dstIp.toIpPrefix();
620 IpAddress srcNextHop = srcIp;
621 IpAddress dstNextHop = dstIp;
622 MacAddress treatmentSrcMac = ethPkt.getDestinationMAC();
623 int borderRoutePrefixLength = 0;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900624 boolean updateMac = simpleFabric.isVMac(ethPkt.getDestinationMAC());
625
626 // check subnet local or route
627 IpSubnet srcSubnet = simpleFabric.findIpSubnet(srcIp);
628 if (srcSubnet == null) {
629 Route route = simpleFabric.findBorderRoute(srcIp);
630 if (route == null) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900631 log.warn("unknown srcIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
632 srcCp, srcIp, dstIp, ipProto);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900633 return;
634 }
635 srcPrefix = route.prefix();
636 srcNextHop = route.nextHop();
637 borderRoutePrefixLength = route.prefix().prefixLength();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900638 }
639 IpSubnet dstSubnet = simpleFabric.findIpSubnet(dstIp);
640 if (dstSubnet == null) {
641 Route route = simpleFabric.findBorderRoute(dstIp);
642 if (route == null) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900643 log.warn("unknown dstIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
644 srcCp, srcIp, dstIp, ipProto);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900645 return;
646 }
647 dstPrefix = route.prefix();
648 dstNextHop = route.nextHop();
649 borderRoutePrefixLength = route.prefix().prefixLength();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900650 }
651
652 if (dstSubnet != null) {
653 // destination is local subnet ip
654 if (SimpleFabricService.ALLOW_ETH_ADDRESS_SELECTOR && dstSubnet.equals(srcSubnet)) {
655 // NOTE: if ALLOW_ETH_ADDRESS_SELECTOR=false; l2Forward is always false
656 L2Network l2Network = simpleFabric.findL2Network(dstSubnet.l2NetworkName());
657 treatmentSrcMac = ethPkt.getSourceMAC();
658 if (l2Network != null && l2Network.l2Forward()) {
659 // NOTE: no reactive route action but do forward packet for L2Forward do not handle packet
660 // update mac only if dstMac is virtualGatewayMac, else assume valid mac already for the l2 network
661 log.info("LOCAL FORWARD ONLY: "
662 + "srcCp={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
663 context.inPacket().receivedFrom(),
664 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
665 ethPkt.getVlanID(), ipProto, updateMac);
666 forwardPacketToDstIp(context, dstIp, treatmentSrcMac, updateMac);
667 return;
668 }
669 }
670 encap = dstSubnet.encapsulation();
671 if (encap == EncapsulationType.NONE && srcSubnet != null) {
672 encap = srcSubnet.encapsulation();
673 }
674 } else {
675 // destination is external network
676 if (srcSubnet == null) {
677 // both are externel network
678 log.warn("INVALID PACKET: srcIp and dstIp are both NON-LOCAL: "
679 + "srcCP={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
680 context.inPacket().receivedFrom(),
681 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
682 ethPkt.getVlanID(), ipProto, updateMac);
683 return;
684 }
685 encap = srcSubnet.encapsulation();
686 }
687
688 log.info("REGI AND FORWARD: "
689 + "srcCP={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
690 context.inPacket().receivedFrom(),
691 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
692 ethPkt.getVlanID(), ipProto, updateMac);
693 setUpConnectivity(srcCp, ipProto, srcPrefix, dstPrefix, dstNextHop, treatmentSrcMac, encap, updateMac,
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900694 dstSubnet != null, borderRoutePrefixLength);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900695 forwardPacketToDstIp(context, dstNextHop, treatmentSrcMac, updateMac);
696 }
697
698 /**
699 * Emits the specified packet onto the network.
700 */
701 private void forwardPacketToDstIp(PacketContext context, IpAddress nextHopIp,
702 MacAddress srcMac, boolean updateMac) {
703 Set<Host> hosts = hostService.getHostsByIp(nextHopIp);
704 Host dstHost;
705 if (!hosts.isEmpty()) {
706 dstHost = hosts.iterator().next();
707 } else {
708 // NOTE: hostService.requestMac(nextHopIp); NOT IMPLEMENTED in ONOS HostManager.java; do it myself
709 log.warn("forward packet nextHopIp host_mac unknown: nextHopIp={}", nextHopIp);
710 hostService.startMonitoringIp(nextHopIp);
711 simpleFabric.requestMac(nextHopIp);
712 // CONSIDER: make flood on all port of the dstHost's L2Network
713 return;
714 }
715 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
716 .setOutput(dstHost.location().port()).build();
717 OutboundPacket outPacket;
718 if (updateMac) {
719 // NOTE: eth address update by treatment is NOT applied, so update mac myself
720 outPacket = new DefaultOutboundPacket(dstHost.location().deviceId(), treatment,
721 ByteBuffer.wrap(context.inPacket().parsed()
722 .setSourceMACAddress(srcMac)
723 .setDestinationMACAddress(dstHost.mac()).serialize()));
724 } else {
725 outPacket = new DefaultOutboundPacket(dstHost.location().deviceId(), treatment,
726 context.inPacket().unparsed());
727 }
728 // be quiet on normal situation
729 log.info("forward packet: nextHopIP={} srcCP={} dstCP={}",
730 nextHopIp, context.inPacket().receivedFrom(), dstHost.location());
731 packetService.emit(outPacket);
732 }
733
734 /**
735 * Update intents for connectivity.
736 *
737 * ToHost: dstPrefix = dstHostIp.toIpPrefix(), nextHopIp = destHostIp
738 * ToInternet: dstPrefix = route.prefix(), nextHopIp = route.nextHopIp
739 * returns intent submited or not
740 */
741 private boolean setUpConnectivity(ConnectPoint srcCp, byte ipProto, IpPrefix srcPrefix, IpPrefix dstPrefix,
742 IpAddress nextHopIp, MacAddress treatmentSrcMac,
743 EncapsulationType encap, boolean updateMac,
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900744 boolean isDstLocalSubnet, int borderRoutePrefixLength) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900745 if (!(simpleFabric.findL2Network(srcCp, VlanId.NONE) != null ||
746 (simpleFabric.REACTIVE_ALLOW_LINK_CP && !linkService.getIngressLinks(srcCp).isEmpty()))) {
747 log.warn("NO REGI for srcCp not in L2Network; srcCp={} srcPrefix={} dstPrefix={} nextHopIp={}",
748 srcCp, srcPrefix, dstPrefix, nextHopIp);
749 return false;
750 }
751
752 MacAddress nextHopMac = null;
753 ConnectPoint egressPoint = null;
754 for (Host host : hostService.getHostsByIp(nextHopIp)) {
755 if (host.mac() != null) {
756 nextHopMac = host.mac();
757 egressPoint = host.location();
758 break;
759 }
760 }
761 if (nextHopMac == null || egressPoint == null) {
762 log.info("NO REGI for unknown nextHop Cp and Mac: srcPrefix={} dstPrefix={} nextHopIp={}",
763 srcPrefix, dstPrefix, nextHopIp);
764 hostService.startMonitoringIp(nextHopIp);
765 simpleFabric.requestMac(nextHopIp);
766 return false;
767 }
768 TrafficTreatment treatment;
769 if (updateMac && simpleFabric.ALLOW_ETH_ADDRESS_SELECTOR) {
770 treatment = generateSetMacTreatment(nextHopMac, treatmentSrcMac);
771 } else {
772 treatment = DefaultTrafficTreatment.builder().build();
773 }
774
775 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
776 if (dstPrefix.isIp4()) {
777 selector.matchEthType(Ethernet.TYPE_IPV4);
778 if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE && srcPrefix.prefixLength() > 0) {
779 selector.matchIPSrc(srcPrefix);
780 }
781 if (dstPrefix.prefixLength() > 0) {
782 selector.matchIPDst(dstPrefix);
783 }
784 if (ipProto != 0 && simpleFabric.REACTIVE_MATCH_IP_PROTO) {
785 selector.matchIPProtocol(ipProto);
786 }
787 } else {
788 selector.matchEthType(Ethernet.TYPE_IPV6);
789 if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE && srcPrefix.prefixLength() > 0) {
790 selector.matchIPv6Src(srcPrefix);
791 }
792 if (dstPrefix.prefixLength() > 0) {
793 selector.matchIPv6Dst(dstPrefix);
794 }
795 if (ipProto != 0 && simpleFabric.REACTIVE_MATCH_IP_PROTO) {
796 selector.matchIPProtocol(ipProto);
797 }
798 }
799
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900800 Key key;
801 String keyProtoTag = "";
802 if (simpleFabric.REACTIVE_MATCH_IP_PROTO) {
803 keyProtoTag = "-p" + ipProto;
804 }
805 if (simpleFabric.REACTIVE_SINGLE_TO_SINGLE) {
806 // allocate intent per (srcPrefix, dstPrefix)
807 key = Key.of(srcPrefix.toString() + "-to-" + dstPrefix.toString() + keyProtoTag, reactiveAppId);
808 } else {
809 // allocate intent per (srcDeviceId, dstPrefix)
810 key = Key.of(srcCp.deviceId().toString() + "-to-" + dstPrefix.toString() + keyProtoTag, reactiveAppId);
811 }
812
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900813 // check and merge already existing ingress points
Ray Milkey27cff8c2018-03-05 14:58:26 -0800814 Set<FilteredConnectPoint> ingressPoints = new HashSet<>();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900815 MultiPointToSinglePointIntent existingIntent = (MultiPointToSinglePointIntent) intentService.getIntent(key);
816 if (existingIntent != null) {
Ray Milkey27cff8c2018-03-05 14:58:26 -0800817 ingressPoints.addAll(existingIntent.filteredIngressPoints());
818 if (!ingressPoints.add(new FilteredConnectPoint(srcCp)) // alread exists and dst not changed
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900819 && egressPoint.equals(existingIntent.egressPoint())
820 && treatment.equals(existingIntent.treatment())) {
821 log.warn("srcCP is already in mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
822 srcPrefix, dstPrefix, srcCp);
823 return false;
824 }
825 log.info("update mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
826 srcPrefix, dstPrefix, srcCp);
827 } else {
828 log.info("create mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
829 srcPrefix, dstPrefix, srcCp);
Ray Milkey27cff8c2018-03-05 14:58:26 -0800830 ingressPoints.add(new FilteredConnectPoint(srcCp));
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900831 }
832
833 // priority for forwarding case
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900834 int priority = reactivePriority(true, isDstLocalSubnet, borderRoutePrefixLength);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900835
836 MultiPointToSinglePointIntent newIntent = MultiPointToSinglePointIntent.builder()
837 .key(key)
838 .appId(reactiveAppId)
839 .selector(selector.build())
840 .treatment(treatment)
Ray Milkey27cff8c2018-03-05 14:58:26 -0800841 .filteredIngressPoints(ingressPoints)
842 .filteredEgressPoint(new FilteredConnectPoint(egressPoint))
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900843 .priority(priority)
844 .constraints(buildConstraints(reactiveConstraints, encap))
845 .build();
846 log.info("submmit mp2p intent: srcPrefix={} dstPrefix={} srcCp={} "
847 + "newIntent={} nextHopIp={} nextHopMac={} priority={}",
848 srcPrefix, dstPrefix, ingressPoints, newIntent, nextHopIp, nextHopMac, priority);
849 toBePurgedIntentKeys.remove(newIntent.key());
850 intentService.submit(newIntent);
851 return true;
852 }
853
854 // generate treatment to target
855 private TrafficTreatment generateSetMacTreatment(MacAddress dstMac, MacAddress srcMac) {
856 return DefaultTrafficTreatment.builder()
857 // NOTE: Cisco Switch requires both src and dst mac set
858 .setEthDst(dstMac)
859 .setEthSrc(srcMac)
860 .build();
861 }
862
863 // monitor border peers for routeService lookup to be effective
864 private void monitorBorderPeers() {
865 for (Route route : simpleFabric.getBorderRoutes()) {
866 hostService.startMonitoringIp(route.nextHop());
867 simpleFabric.requestMac(route.nextHop());
868 }
869 }
870
871 // priority calculator
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900872 private int reactivePriority(boolean isForward, boolean isDstLocalSubnet, int borderRoutePrefixLength) {
873 if (isDstLocalSubnet) { // -> dst:localSubnet
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900874 if (isForward) {
875 return simpleFabric.PRI_REACTIVE_LOCAL_FORWARD;
876 } else { // isInterncept
877 return simpleFabric.PRI_REACTIVE_LOCAL_INTERCEPT;
878 }
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900879 } else { // -> dst:boarderRouteNextHop
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900880 int offset;
881 if (isForward) {
882 offset = simpleFabric.PRI_REACTIVE_BORDER_FORWARD;
883 } else { // isIntercept
884 offset = simpleFabric.PRI_REACTIVE_BORDER_INTERCEPT;
885 }
886 return simpleFabric.PRI_REACTIVE_BORDER_BASE
887 + borderRoutePrefixLength * simpleFabric.PRI_REACTIVE_BORDER_STEP + offset;
888 }
889 }
890
891 // constraints generator
892 private List<Constraint> buildConstraints(List<Constraint> constraints, EncapsulationType encap) {
893 if (!encap.equals(EncapsulationType.NONE)) {
894 List<Constraint> newConstraints = new ArrayList<>(constraints);
895 constraints.stream()
896 .filter(c -> c instanceof EncapsulationConstraint)
897 .forEach(newConstraints::remove);
898 newConstraints.add(new EncapsulationConstraint(encap));
899 return ImmutableList.copyOf(newConstraints);
900 }
901 return constraints;
902 }
903
904 // Dump Cli Handler
905 private void dump(String subject, PrintStream out) {
Ray Milkey2ff67162018-01-22 10:14:19 -0800906 if ("intents".equals(subject)) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900907 out.println("Reactive Routing Route Intents:\n");
908 for (Intent entry : intentService.getIntents()) {
909 if (reactiveAppId.equals(entry.appId())) {
910 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
911 out.println(" " + intent.key().toString()
912 + " to " + intent.egressPoint().toString()
913 + " set " + intent.treatment().immediate().toString()
914 + " from " + intent.ingressPoints().toString());
915 }
916 }
917 out.println("");
918
919 out.println("Reactive Routing Intercept Flow Rules:\n");
920 List<FlowRule> rules = new ArrayList(interceptFlowRules);
921 Collections.sort(rules, new Comparator<FlowRule>() {
922 @Override
923 public int compare(FlowRule a, FlowRule b) {
924 int r = a.deviceId().toString().compareTo(b.deviceId().toString());
925 return (r != 0) ? r : Integer.compare(b.priority(), a.priority()); // descending on priority
926 }
927 });
928 for (FlowRule rule : rules) {
929 out.println(" device=" + rule.deviceId().toString()
930 + " priority=" + rule.priority()
931 + " selector=" + rule.selector().criteria().toString());
932 }
933 out.println("");
934 out.println("Reactive Routing Intents to Be Purged:\n");
935 for (Key key: toBePurgedIntentKeys) {
936 out.println(" " + key.toString());
937 }
938 out.println("");
939
Ray Milkey2ff67162018-01-22 10:14:19 -0800940 } else if ("reactive-intents".equals(subject)) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900941 for (Intent entry : intentService.getIntents()) {
942 if (reactiveAppId.equals(entry.appId())) {
943 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
944 out.println(intent.key().toString()
945 + " to " + intent.egressPoint().toString()
946 + " set " + intent.treatment().immediate().toString()
947 + " from " + intent.ingressPoints().toString());
948 }
949 }
950 }
951 }
952
953 // Listener
954 private class InternalSimpleFabricListener implements SimpleFabricListener {
955 @Override
956 public void event(SimpleFabricEvent event) {
957 switch (event.type()) {
958 case SIMPLE_FABRIC_UPDATED:
959 refreshIntercepts();
960 refreshRouteIntents();
961 checkIntentsPurge();
962 break;
963 case SIMPLE_FABRIC_FLUSH:
964 withdrawAllReactiveIntents();
965 checkIntentsPurge();
966 break;
967 case SIMPLE_FABRIC_IDLE:
968 refreshIntercepts();
969 refreshRouteIntents();
970 checkIntentsPurge();
971 monitorBorderPeers();
972 break;
973 case SIMPLE_FABRIC_DUMP:
974 dump(event.subject(), event.out());
975 break;
976 default:
977 break;
978 }
979 }
980 }
981
982}
983