blob: cbca9a59869256070289e31006de15682e66d08d [file] [log] [blame]
Lee Yongjae7c27bb42017-11-17 12:00:45 +09001/*
Jian Li8df54a92018-08-23 17:01:31 +09002 * Copyright 2018-present Open Networking Foundation
Lee Yongjae7c27bb42017-11-17 12:00:45 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jian Li8df54a92018-08-23 17:01:31 +090016package org.onosproject.simplefabric.impl;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090017
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;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090030import org.onlab.packet.Ip4Address;
31import org.onlab.packet.Ip4Prefix;
32import org.onlab.packet.Ip6Address;
33import org.onlab.packet.Ip6Prefix;
Jian Li8df54a92018-08-23 17:01:31 +090034import org.onlab.packet.IpAddress;
35import org.onlab.packet.IpPrefix;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090036import org.onlab.packet.MacAddress;
37import org.onlab.packet.VlanId;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090040import org.onosproject.net.ConnectPoint;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090041import org.onosproject.net.Device;
42import org.onosproject.net.DeviceId;
Jian Li8df54a92018-08-23 17:01:31 +090043import org.onosproject.net.EncapsulationType;
44import org.onosproject.net.FilteredConnectPoint;
45import org.onosproject.net.Host;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090046import org.onosproject.net.device.DeviceService;
47import org.onosproject.net.flow.DefaultFlowRule;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090050import org.onosproject.net.flow.FlowRule;
51import org.onosproject.net.flow.FlowRuleService;
Jian Li8df54a92018-08-23 17:01:31 +090052import org.onosproject.net.flow.TrafficSelector;
53import org.onosproject.net.flow.TrafficTreatment;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090054import org.onosproject.net.host.HostService;
55import org.onosproject.net.intent.Constraint;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090056import org.onosproject.net.intent.Intent;
57import org.onosproject.net.intent.IntentService;
58import org.onosproject.net.intent.Key;
59import org.onosproject.net.intent.MultiPointToSinglePointIntent;
Jian Li8df54a92018-08-23 17:01:31 +090060import org.onosproject.net.intent.constraint.EncapsulationConstraint;
61import org.onosproject.net.intent.constraint.HashedPathSelectionConstraint;
62import org.onosproject.net.intent.constraint.PartialFailureConstraint;
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +090063import org.onosproject.net.intf.Interface;
Jian Li8df54a92018-08-23 17:01:31 +090064import org.onosproject.net.intf.InterfaceService;
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;
Jian Lie2d87512018-08-23 17:33:05 +090073import org.onosproject.simplefabric.api.FabricNetwork;
74import org.onosproject.simplefabric.api.FabricSubnet;
75import org.onosproject.simplefabric.api.FabricRoute;
Jian Lic7efc1d2018-08-23 16:37:34 +090076import org.onosproject.simplefabric.api.SimpleFabricEvent;
77import org.onosproject.simplefabric.api.SimpleFabricListener;
78import org.onosproject.simplefabric.api.SimpleFabricService;
Lee Yongjae7c27bb42017-11-17 12:00:45 +090079import org.slf4j.Logger;
80import org.slf4j.LoggerFactory;
81
82import java.io.PrintStream;
83import java.nio.ByteBuffer;
84import java.util.ArrayList;
85import java.util.Collections;
86import java.util.Comparator;
87import java.util.HashSet;
88import java.util.List;
89import java.util.Set;
90
Jian Li8df54a92018-08-23 17:01:31 +090091import static org.onosproject.simplefabric.api.Constants.ALLOW_ETH_ADDRESS_SELECTOR;
92import static org.onosproject.simplefabric.api.Constants.ALLOW_IPV6;
93import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_BORDER_BASE;
94import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_BORDER_FORWARD;
95import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_BORDER_INTERCEPT;
96import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_BORDER_STEP;
97import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_LOCAL_FORWARD;
98import static org.onosproject.simplefabric.api.Constants.PRI_REACTIVE_LOCAL_INTERCEPT;
99import static org.onosproject.simplefabric.api.Constants.REACTIVE_ALLOW_LINK_CP;
Jian Lie2d87512018-08-23 17:33:05 +0900100import static org.onosproject.simplefabric.api.Constants.ROUTING_APP_ID;
Jian Li8df54a92018-08-23 17:01:31 +0900101import static org.onosproject.simplefabric.api.Constants.REACTIVE_HASHED_PATH_SELECTION;
102import static org.onosproject.simplefabric.api.Constants.REACTIVE_MATCH_IP_PROTO;
103import static org.onosproject.simplefabric.api.Constants.REACTIVE_SINGLE_TO_SINGLE;
104
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900105
106/**
Jian Lie2d87512018-08-23 17:33:05 +0900107 * SimpleFabricRouting handles Routing.
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900108 */
109@Component(immediate = true, enabled = false)
Jian Lie2d87512018-08-23 17:33:05 +0900110public class SimpleFabricRouting {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900111
112 private final Logger log = LoggerFactory.getLogger(getClass());
Jian Lie2d87512018-08-23 17:33:05 +0900113 private ApplicationId appId;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900116 protected CoreService coreService;
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900119 protected PacketService packetService;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900122 protected InterfaceService interfaceService;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900125 protected DeviceService deviceService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900128 protected LinkService linkService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900131 protected HostService hostService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900134 protected IntentService intentService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900137 protected FlowRuleService flowRuleService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900140 protected SimpleFabricService simpleFabric;
141
142 private ImmutableList<Constraint> reactiveConstraints
143 = ImmutableList.of(new PartialFailureConstraint());
144 //= ImmutableList.of();
145 // NOTE: SHOULD NOT use HashedPathSelectionConstraint
146 // for unpredictable srcCp of Link appears as reactive packet traffic
147
148 private Set<FlowRule> interceptFlowRules = new HashSet<>();
149 private Set<Key> toBePurgedIntentKeys = new HashSet<>();
150 // NOTE: manage purged intents by key for intentService.getIntent() supports key only
151
152 private final InternalSimpleFabricListener simpleFabricListener = new InternalSimpleFabricListener();
Jian Lie2d87512018-08-23 17:33:05 +0900153 private InternalRoutingProcessor processor = new InternalRoutingProcessor();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900154
155 @Activate
156 public void activate() {
Jian Lie2d87512018-08-23 17:33:05 +0900157 appId = coreService.registerApplication(ROUTING_APP_ID);
158 log.info("simple fabric routing starting with app id {}", appId.toString());
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900159
160 // NOTE: may not clear at init for MIGHT generate pending_remove garbages
161 // use flush event from simple fabric cli command
162
Jian Li8df54a92018-08-23 17:01:31 +0900163 if (REACTIVE_HASHED_PATH_SELECTION) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900164 reactiveConstraints = ImmutableList.of(new PartialFailureConstraint(),
165 new HashedPathSelectionConstraint());
166 } else {
167 reactiveConstraints = ImmutableList.of(new PartialFailureConstraint());
168 }
169
Jian Lie2d87512018-08-23 17:33:05 +0900170 processor = new InternalRoutingProcessor();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900171 packetService.addProcessor(processor, PacketProcessor.director(2));
172 simpleFabric.addListener(simpleFabricListener);
173
174 registerIntercepts();
175 refreshIntercepts();
176
Jian Lie2d87512018-08-23 17:33:05 +0900177 log.info("simple fabric routing started");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900178 }
179
180 @Deactivate
181 public void deactivate() {
Jian Lie2d87512018-08-23 17:33:05 +0900182 log.info("simple fabric routing stopping");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900183
184 packetService.removeProcessor(processor);
185 simpleFabric.removeListener(simpleFabricListener);
186
187 withdrawIntercepts();
188
189 // NOTE: may not clear at init for MIGHT generate pending_remove garbages
190 // use flush event from simple fabric cli command
191
192 toBePurgedIntentKeys.clear();
193
Jian Lie2d87512018-08-23 17:33:05 +0900194 flowRuleService.removeFlowRulesById(appId);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900195
196 processor = null;
197
Jian Lie2d87512018-08-23 17:33:05 +0900198 log.info("simple fabric routing stopped");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900199 }
200
201 /**
202 * Request packet in via the PacketService.
203 */
204 private void registerIntercepts() {
205 // register default intercepts on packetService for broder routing intercepts
206
207 packetService.requestPackets(
208 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV4).build(),
Jian Lie2d87512018-08-23 17:33:05 +0900209 PacketPriority.REACTIVE, appId);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900210
Jian Li8df54a92018-08-23 17:01:31 +0900211 if (ALLOW_IPV6) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900212 packetService.requestPackets(
213 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV6).build(),
Jian Lie2d87512018-08-23 17:33:05 +0900214 PacketPriority.REACTIVE, appId);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900215 }
216
Jian Lie2d87512018-08-23 17:33:05 +0900217 log.info("simple fabric routing ip packet intercepts started");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900218 }
219
220 /**
221 * Cancel request for packet in via PacketService.
222 */
223 private void withdrawIntercepts() {
224 // unregister default intercepts on packetService
225
226 packetService.cancelPackets(
227 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV4).build(),
Jian Lie2d87512018-08-23 17:33:05 +0900228 PacketPriority.REACTIVE, appId);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900229
Jian Li8df54a92018-08-23 17:01:31 +0900230 if (ALLOW_IPV6) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900231 packetService.cancelPackets(
232 DefaultTrafficSelector.builder().matchEthType(Ethernet.TYPE_IPV6).build(),
Jian Lie2d87512018-08-23 17:33:05 +0900233 PacketPriority.REACTIVE, appId);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900234 }
235
Jian Lie2d87512018-08-23 17:33:05 +0900236 log.info("simple fabric routing ip packet intercepts stopped");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900237 }
238
239 /**
Jian Lie2d87512018-08-23 17:33:05 +0900240 * Refresh device flow rules for intercepts on local fabricSubnets.
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900241 */
242 private void refreshIntercepts() {
243 Set<FlowRule> newInterceptFlowRules = new HashSet<>();
244 for (Device device : deviceService.getAvailableDevices()) {
Jian Lie2d87512018-08-23 17:33:05 +0900245 for (FabricSubnet subnet : simpleFabric.defaultFabricSubnets()) {
246 newInterceptFlowRules.add(generateInterceptFlowRule(true, device.id(), subnet.prefix()));
247 // check if this devices has the fabricSubnet, then add ip broadcast flue rule
Jian Lida0b4852018-08-29 20:40:44 +0900248 FabricNetwork fabricNetwork = simpleFabric.fabricNetwork(subnet.networkName());
Jian Lie2d87512018-08-23 17:33:05 +0900249 if (fabricNetwork != null && fabricNetwork.contains(device.id())) {
250 newInterceptFlowRules.add(generateLocalSubnetIpBctFlowRule(device.id(), subnet.prefix(),
251 fabricNetwork));
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900252 }
253 // JUST FOR FLOW RULE TEST ONLY
254 //newInterceptFlowRules.add(generateTestFlowRule(device.id(), subnet.ipPrefix()));
255 }
Jian Lie2d87512018-08-23 17:33:05 +0900256 for (FabricRoute route : simpleFabric.fabricRoutes()) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900257 newInterceptFlowRules.add(generateInterceptFlowRule(false, device.id(), route.prefix()));
258 }
259 }
260
261 if (!newInterceptFlowRules.equals(interceptFlowRules)) {
262 // NOTE: DO NOT REMOVE INTERCEPT FLOW RULES FOR FAILED DEVICE FLOW UPDATE MIGHT BE BLOCKED
263 /*
264 interceptFlowRules.stream()
265 .filter(rule -> !newInterceptFlowRules.contains(rule))
266 .forEach(rule -> {
267 flowRuleService.removeFlowRules(rule);
268 log.info("simple fabric reactive routing remove intercept flow rule: {}", rule);
269 });
270 */
271 newInterceptFlowRules.stream()
272 .filter(rule -> !interceptFlowRules.contains(rule))
273 .forEach(rule -> {
274 flowRuleService.applyFlowRules(rule);
Jian Lie2d87512018-08-23 17:33:05 +0900275 log.info("simple fabric routing apply intercept flow rule: {}", rule);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900276 });
277 interceptFlowRules = newInterceptFlowRules;
278 }
279 }
280
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900281 private FlowRule generateInterceptFlowRule(boolean isDstLocalSubnet, DeviceId deviceId, IpPrefix prefix) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900282 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
283 if (prefix.isIp4()) {
284 selector.matchEthType(Ethernet.TYPE_IPV4);
285 if (prefix.prefixLength() > 0) {
286 selector.matchIPDst(prefix);
287 }
288 } else {
289 selector.matchEthType(Ethernet.TYPE_IPV6);
290 if (prefix.prefixLength() > 0) {
291 selector.matchIPv6Dst(prefix);
292 }
293 }
294 FlowRule rule = DefaultFlowRule.builder()
295 .forDevice(deviceId)
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900296 .withPriority(reactivePriority(false, isDstLocalSubnet, prefix.prefixLength()))
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900297 .withSelector(selector.build())
298 .withTreatment(DefaultTrafficTreatment.builder().punt().build())
Jian Lie2d87512018-08-23 17:33:05 +0900299 .fromApp(appId)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900300 .makePermanent()
301 .forTable(0).build();
302 return rule;
303 }
304
Jian Lie2d87512018-08-23 17:33:05 +0900305 private FlowRule generateLocalSubnetIpBctFlowRule(DeviceId deviceId, IpPrefix prefix, FabricNetwork fabricNetwork) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900306 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
307 IpPrefix bctPrefix;
308 if (prefix.isIp4()) {
309 bctPrefix = Ip4Prefix.valueOf(prefix.getIp4Prefix().address().toInt() |
310 ~Ip4Address.makeMaskPrefix(prefix.prefixLength()).toInt(),
311 Ip4Address.BIT_LENGTH);
312 selector.matchEthType(Ethernet.TYPE_IPV4);
313 selector.matchIPDst(bctPrefix);
314 } else {
315 byte[] p = prefix.getIp6Prefix().address().toOctets();
316 byte[] m = Ip6Address.makeMaskPrefix(prefix.prefixLength()).toOctets();
317 for (int i = 0; i < p.length; i++) {
318 p[i] |= ~m[i];
319 }
320 bctPrefix = Ip6Prefix.valueOf(p, Ip6Address.BIT_LENGTH);
321 selector.matchEthType(Ethernet.TYPE_IPV6);
322 selector.matchIPv6Dst(bctPrefix);
323 }
324 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
325 Set<ConnectPoint> newEgressPoints = new HashSet<>();
Jian Lie2d87512018-08-23 17:33:05 +0900326 for (Interface iface : fabricNetwork.interfaces()) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900327 if (iface.connectPoint().deviceId().equals(deviceId)) {
328 treatment.setOutput(iface.connectPoint().port());
329 }
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900330 }
331 FlowRule rule = DefaultFlowRule.builder()
332 .forDevice(deviceId)
333 .withPriority(reactivePriority(true, true, bctPrefix.prefixLength()))
334 .withSelector(selector.build())
335 .withTreatment(treatment.build())
Jian Lie2d87512018-08-23 17:33:05 +0900336 .fromApp(appId)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900337 .makePermanent()
338 .forTable(0).build();
339 return rule;
340 }
341
342 /**
343 * Refresh routes by examining network resource status.
344 */
345 private void refreshRouteIntents() {
346 for (Intent entry : intentService.getIntents()) {
Jian Lie2d87512018-08-23 17:33:05 +0900347 if (!appId.equals(entry.appId())) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900348 continue;
349 }
350
351 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
352
353 if (!intentService.isLocal(intent.key())) {
354 if (toBePurgedIntentKeys.contains(intent.key())) {
355 toBePurgedIntentKeys.remove(intent.key()); // clear non local intent
356 }
357 continue;
358 }
359
360 try {
361 switch (intentService.getIntentState(intent.key())) {
362 //case FAILED: // failed intent is not auto removed
363 case WITHDRAWN:
364 log.warn("intent found failed or withdrawn; "
365 + "remove and try to purge intent: key={}", intent.key());
366 // purge intents here without withdraw
367 intentService.purge(intentService.getIntent(intent.key()));
368 toBePurgedIntentKeys.add(intent.key());
369 continue;
370 default: // no action
371 break;
372 }
373 } catch (Exception e) {
374 log.warn("intent status lookup failed: error={}", e);
375 continue; // this intent seems invalid; no action
376 }
377
378 // dummy loop to break on remove cases
379 if (!deviceService.isAvailable(intent.egressPoint().deviceId())) {
380 log.info("refresh route intents; remove intent for no device: key={}", intent.key());
381 intentService.withdraw(intentService.getIntent(intent.key()));
382 toBePurgedIntentKeys.add(intent.key());
383 continue;
384 }
Jian Lie2d87512018-08-23 17:33:05 +0900385 if (!(simpleFabric.fabricNetwork(intent.egressPoint(), VlanId.NONE) != null ||
Jian Li8df54a92018-08-23 17:01:31 +0900386 (REACTIVE_ALLOW_LINK_CP &&
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900387 !linkService.getEgressLinks(intent.egressPoint()).isEmpty()))) {
388 log.info("refresh route intents; remove intent for egress point not available: key={}", intent.key());
389 intentService.withdraw(intentService.getIntent(intent.key()));
390 toBePurgedIntentKeys.add(intent.key());
391 continue;
392 }
393
394 // MAY NEED TO CHECK: intent.egressPoint and intent.treatment's dstMac is valid against hosts
Jian Li8df54a92018-08-23 17:01:31 +0900395 if (REACTIVE_SINGLE_TO_SINGLE && !REACTIVE_ALLOW_LINK_CP) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900396 // single path intent only; no need to check ingress points
397 continue;
398 }
399
Ray Milkey27cff8c2018-03-05 14:58:26 -0800400 Set<FilteredConnectPoint> newIngressPoints = new HashSet<>();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900401 boolean ingressPointChanged = false;
Ray Milkey27cff8c2018-03-05 14:58:26 -0800402 for (FilteredConnectPoint cp : intent.filteredIngressPoints()) {
403 if (deviceService.isAvailable(cp.connectPoint().deviceId()) &&
Jian Lie2d87512018-08-23 17:33:05 +0900404 (simpleFabric.fabricNetwork(cp.connectPoint(), VlanId.NONE) != null ||
Jian Li8df54a92018-08-23 17:01:31 +0900405 (REACTIVE_ALLOW_LINK_CP &&
Ray Milkey27cff8c2018-03-05 14:58:26 -0800406 !linkService.getIngressLinks(cp.connectPoint()).isEmpty()))) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900407 newIngressPoints.add(cp);
408 } else {
409 log.info("refresh route ingress cp of "
410 + "not in 2Networks nor links: {}", cp);
411 ingressPointChanged = true;
412 }
413 }
414 if (newIngressPoints.isEmpty()) {
415 log.info("refresh route intents; "
416 + "remove intent for no ingress nor egress point available: key={}", intent.key());
417 intentService.withdraw(intentService.getIntent(intent.key()));
418 toBePurgedIntentKeys.add(intent.key());
419 continue;
420 }
421 // update ingress points
422 if (ingressPointChanged) {
423 MultiPointToSinglePointIntent updatedIntent =
424 MultiPointToSinglePointIntent.builder()
Jian Lie2d87512018-08-23 17:33:05 +0900425 .appId(appId)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900426 .key(intent.key())
427 .selector(intent.selector())
428 .treatment(intent.treatment())
Ray Milkey27cff8c2018-03-05 14:58:26 -0800429 .filteredIngressPoints(newIngressPoints)
430 .filteredEgressPoint(intent.filteredEgressPoint())
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900431 .priority(intent.priority())
432 .constraints(intent.constraints())
433 .build();
434 log.info("refresh route update intent: key={} updatedIntent={}",
435 intent.key(), updatedIntent);
436 toBePurgedIntentKeys.remove(intent.key()); // may remove from old purged entry
437 intentService.submit(updatedIntent);
438 }
439 }
440 }
441
442 private void checkIntentsPurge() {
443 // check intents to be purge
444 if (!toBePurgedIntentKeys.isEmpty()) {
445 Set<Key> removeKeys = new HashSet<>();
446 for (Key key : toBePurgedIntentKeys) {
447 if (!intentService.isLocal(key)) {
448 removeKeys.add(key);
449 continue;
450 }
451 Intent intentToPurge = intentService.getIntent(key);
452 if (intentToPurge == null) {
453 log.info("purged intent: key={}", key);
454 removeKeys.add(key);
455 } else {
456 switch (intentService.getIntentState(key)) {
457 // case FAILED: // not auto removed
458 case WITHDRAWN:
459 log.info("try to purge intent: key={}", key);
460 intentService.purge(intentToPurge);
461 break;
462 case INSTALL_REQ:
463 case INSTALLED:
464 case INSTALLING:
465 case RECOMPILING:
466 case COMPILING:
467 log.warn("not to purge for active intent: key={}", key);
468 removeKeys.add(key);
469 break;
470 case WITHDRAW_REQ:
471 case WITHDRAWING:
472 case PURGE_REQ:
473 case CORRUPT:
474 default:
475 // no action
476 break;
477 }
478 }
479 }
480 toBePurgedIntentKeys.removeAll(removeKeys);
481 }
482 }
483
484 public void withdrawAllReactiveIntents() {
485 // check all intents of this app
486 // NOTE: cli calls are handling within the cli called node only; so should not user inents.isLocal()
487 Set<Intent> myIntents = new HashSet<>();
488 for (Intent intent : intentService.getIntents()) {
Jian Lie2d87512018-08-23 17:33:05 +0900489 if (appId.equals(intent.appId())) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900490 myIntents.add(intent);
491 }
492 }
493 // withdraw all my intents
494 for (Intent intent : myIntents) {
495 switch (intentService.getIntentState(intent.key())) {
496 case FAILED:
497 intentService.purge(intent);
498 toBePurgedIntentKeys.add(intent.key());
499 break;
500 case WITHDRAWN:
501 intentService.purge(intent);
502 toBePurgedIntentKeys.add(intent.key());
503 break;
504 case INSTALL_REQ:
505 case INSTALLED:
506 case INSTALLING:
507 case RECOMPILING:
508 case COMPILING:
509 intentService.withdraw(intent);
510 toBePurgedIntentKeys.add(intent.key());
511 break;
512 case WITHDRAW_REQ:
513 case WITHDRAWING:
514 toBePurgedIntentKeys.add(intent.key());
515 break;
516 case PURGE_REQ:
517 case CORRUPT:
518 default:
519 // no action
520 break;
521 }
522 }
523 }
524
525 /**
Jian Lie2d87512018-08-23 17:33:05 +0900526 * Internal Packet Handling.
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900527 */
Jian Lie2d87512018-08-23 17:33:05 +0900528 private class InternalRoutingProcessor implements PacketProcessor {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900529 @Override
530 public void process(PacketContext context) {
531 InboundPacket pkt = context.inPacket();
532 Ethernet ethPkt = pkt.parsed();
533 if (ethPkt == null) {
534 return;
535 }
536 ConnectPoint srcCp = pkt.receivedFrom();
537 IpAddress srcIp;
538 IpAddress dstIp;
539 byte ipProto = 0; /* 0 or tcp, udp */
540
541 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
542 case IPV4:
543 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
544 srcIp = IpAddress.valueOf(ipv4Packet.getSourceAddress());
545 dstIp = IpAddress.valueOf(ipv4Packet.getDestinationAddress());
546 ipProto = ipv4Packet.getProtocol();
547 break;
548 case IPV6:
549 IPv6 ipv6Packet = (IPv6) ethPkt.getPayload();
550 srcIp = IpAddress.valueOf(IpAddress.Version.INET6, ipv6Packet.getSourceAddress());
551 dstIp = IpAddress.valueOf(IpAddress.Version.INET6, ipv6Packet.getDestinationAddress());
552 ipProto = ipv6Packet.getNextHeader();
553 break;
554 default:
555 return; // ignore unknow ether type packets
556 }
557 if (ipProto != 6 && ipProto != 17) {
558 ipProto = 0; /* handle special for TCP and UDP only */
559 }
560
561 if (!checkVirtualGatewayIpPacket(pkt, srcIp, dstIp)) {
562 ipPacketReactiveProcessor(context, ethPkt, srcCp, srcIp, dstIp, ipProto);
563 // TODO: add ReactiveRouting for dstIp to srcIp with discovered egressCp as srcCp
564 }
565 }
566 }
567
568 /**
569 * handle Packet with dstIp=virtualGatewayIpAddresses.
570 * returns true(handled) or false(not for virtual gateway)
571 */
572 private boolean checkVirtualGatewayIpPacket(InboundPacket pkt, IpAddress srcIp, IpAddress dstIp) {
573 Ethernet ethPkt = pkt.parsed(); // assume valid
574
Jian Lie2d87512018-08-23 17:33:05 +0900575 MacAddress mac = simpleFabric.vMacForIp(dstIp);
576 if (mac == null || !simpleFabric.isVirtualGatewayMac(ethPkt.getDestinationMAC())) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900577 /* Destination MAC should be any of virtual gateway macs */
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900578 return false;
579 } else if (dstIp.isIp4()) {
580 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
581 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_ICMP) {
582 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
583
584 if (icmpPacket.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
585 log.info("IPV4 ICMP ECHO request to virtual gateway: "
586 + "srcIp={} dstIp={} proto={}", srcIp, dstIp, ipv4Packet.getProtocol());
587 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
588 .setOutput(pkt.receivedFrom().port()).build();
589 OutboundPacket packet =
590 new DefaultOutboundPacket(pkt.receivedFrom().deviceId(), treatment,
591 ByteBuffer.wrap(icmpPacket.buildIcmpReply(pkt.parsed()).serialize()));
592 packetService.emit(packet);
593 return true;
594 }
595 }
596 log.warn("IPV4 packet to virtual gateway dropped: "
597 + "srcIp={} dstIp={} proto={}", srcIp, dstIp, ipv4Packet.getProtocol());
598 return true;
599
600 } else if (dstIp.isIp6()) {
601 // TODO: not tested yet (2017-07-20)
602 IPv6 ipv6Packet = (IPv6) ethPkt.getPayload();
603 if (ipv6Packet.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
604 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
605
606 if (icmp6Packet.getIcmpType() == ICMP6.ECHO_REQUEST) {
607 log.info("IPV6 ICMP6 ECHO request to virtual gateway: srcIp={} dstIp={} nextHeader={}",
608 srcIp, dstIp, ipv6Packet.getNextHeader());
609 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
610 .setOutput(pkt.receivedFrom().port()).build();
611 OutboundPacket packet =
612 new DefaultOutboundPacket(pkt.receivedFrom().deviceId(), treatment,
613 ByteBuffer.wrap(icmp6Packet.buildIcmp6Reply(pkt.parsed()).serialize()));
614 packetService.emit(packet);
615 return true;
616 }
617 }
618 log.warn("IPV6 packet to virtual gateway dropped: srcIp={} dstIp={} nextHeader={}",
619 srcIp, dstIp, ipv6Packet.getNextHeader());
620 return true;
621
622 }
623 return false; // unknown traffic
624 }
625
626 /**
627 * Routes packet reactively.
628 */
629 private void ipPacketReactiveProcessor(PacketContext context, Ethernet ethPkt, ConnectPoint srcCp,
630 IpAddress srcIp, IpAddress dstIp, byte ipProto) {
631 /* check reactive handling and forward packet */
632 log.trace("ip packet: srcCp={} srcIp={} dstIp={} ipProto={}",
633 srcCp, srcIp, dstIp, ipProto);
634
635 EncapsulationType encap = EncapsulationType.NONE;
636
637 // prefix and nextHop for local Subnet
638 IpPrefix srcPrefix = srcIp.toIpPrefix();
639 IpPrefix dstPrefix = dstIp.toIpPrefix();
640 IpAddress srcNextHop = srcIp;
641 IpAddress dstNextHop = dstIp;
642 MacAddress treatmentSrcMac = ethPkt.getDestinationMAC();
643 int borderRoutePrefixLength = 0;
Jian Lie2d87512018-08-23 17:33:05 +0900644 boolean updateMac = simpleFabric.isVirtualGatewayMac(ethPkt.getDestinationMAC());
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900645
646 // check subnet local or route
Jian Lie2d87512018-08-23 17:33:05 +0900647 FabricSubnet srcSubnet = simpleFabric.fabricSubnet(srcIp);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900648 if (srcSubnet == null) {
Jian Lie2d87512018-08-23 17:33:05 +0900649 FabricRoute route = simpleFabric.fabricRoute(srcIp);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900650 if (route == null) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900651 log.warn("unknown srcIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
652 srcCp, srcIp, dstIp, ipProto);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900653 return;
654 }
655 srcPrefix = route.prefix();
656 srcNextHop = route.nextHop();
657 borderRoutePrefixLength = route.prefix().prefixLength();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900658 }
Jian Lie2d87512018-08-23 17:33:05 +0900659 FabricSubnet dstSubnet = simpleFabric.fabricSubnet(dstIp);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900660 if (dstSubnet == null) {
Jian Lie2d87512018-08-23 17:33:05 +0900661 FabricRoute route = simpleFabric.fabricRoute(dstIp);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900662 if (route == null) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900663 log.warn("unknown dstIp; drop: srcCp={} srcIp={} dstIp={} ipProto={}",
664 srcCp, srcIp, dstIp, ipProto);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900665 return;
666 }
667 dstPrefix = route.prefix();
668 dstNextHop = route.nextHop();
669 borderRoutePrefixLength = route.prefix().prefixLength();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900670 }
671
672 if (dstSubnet != null) {
673 // destination is local subnet ip
Jian Li8df54a92018-08-23 17:01:31 +0900674 if (ALLOW_ETH_ADDRESS_SELECTOR && dstSubnet.equals(srcSubnet)) {
Jian Lie2d87512018-08-23 17:33:05 +0900675 // NOTE: if ALLOW_ETH_ADDRESS_SELECTOR=false; isForward is always false
Jian Lida0b4852018-08-29 20:40:44 +0900676 FabricNetwork fabricNetwork = simpleFabric.fabricNetwork(dstSubnet.networkName());
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900677 treatmentSrcMac = ethPkt.getSourceMAC();
Jian Lie2d87512018-08-23 17:33:05 +0900678 if (fabricNetwork != null && fabricNetwork.isForward()) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900679 // NOTE: no reactive route action but do forward packet for L2Forward do not handle packet
680 // update mac only if dstMac is virtualGatewayMac, else assume valid mac already for the l2 network
681 log.info("LOCAL FORWARD ONLY: "
682 + "srcCp={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
683 context.inPacket().receivedFrom(),
684 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
685 ethPkt.getVlanID(), ipProto, updateMac);
686 forwardPacketToDstIp(context, dstIp, treatmentSrcMac, updateMac);
687 return;
688 }
689 }
690 encap = dstSubnet.encapsulation();
691 if (encap == EncapsulationType.NONE && srcSubnet != null) {
692 encap = srcSubnet.encapsulation();
693 }
694 } else {
695 // destination is external network
696 if (srcSubnet == null) {
697 // both are externel network
698 log.warn("INVALID PACKET: srcIp and dstIp are both NON-LOCAL: "
699 + "srcCP={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
700 context.inPacket().receivedFrom(),
701 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
702 ethPkt.getVlanID(), ipProto, updateMac);
703 return;
704 }
705 encap = srcSubnet.encapsulation();
706 }
707
708 log.info("REGI AND FORWARD: "
709 + "srcCP={} srcIp={} dstIp={} srcMac={} dstMac={} vlanId={} ipProto={} updateMac={}",
710 context.inPacket().receivedFrom(),
711 srcIp, dstIp, ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
712 ethPkt.getVlanID(), ipProto, updateMac);
713 setUpConnectivity(srcCp, ipProto, srcPrefix, dstPrefix, dstNextHop, treatmentSrcMac, encap, updateMac,
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900714 dstSubnet != null, borderRoutePrefixLength);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900715 forwardPacketToDstIp(context, dstNextHop, treatmentSrcMac, updateMac);
716 }
717
718 /**
719 * Emits the specified packet onto the network.
720 */
721 private void forwardPacketToDstIp(PacketContext context, IpAddress nextHopIp,
722 MacAddress srcMac, boolean updateMac) {
723 Set<Host> hosts = hostService.getHostsByIp(nextHopIp);
724 Host dstHost;
725 if (!hosts.isEmpty()) {
726 dstHost = hosts.iterator().next();
727 } else {
728 // NOTE: hostService.requestMac(nextHopIp); NOT IMPLEMENTED in ONOS HostManager.java; do it myself
729 log.warn("forward packet nextHopIp host_mac unknown: nextHopIp={}", nextHopIp);
730 hostService.startMonitoringIp(nextHopIp);
731 simpleFabric.requestMac(nextHopIp);
Jian Lie2d87512018-08-23 17:33:05 +0900732 // CONSIDER: make flood on all port of the dstHost's DefaultFabricNetwork
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900733 return;
734 }
735 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
736 .setOutput(dstHost.location().port()).build();
737 OutboundPacket outPacket;
738 if (updateMac) {
739 // NOTE: eth address update by treatment is NOT applied, so update mac myself
740 outPacket = new DefaultOutboundPacket(dstHost.location().deviceId(), treatment,
741 ByteBuffer.wrap(context.inPacket().parsed()
742 .setSourceMACAddress(srcMac)
743 .setDestinationMACAddress(dstHost.mac()).serialize()));
744 } else {
745 outPacket = new DefaultOutboundPacket(dstHost.location().deviceId(), treatment,
746 context.inPacket().unparsed());
747 }
748 // be quiet on normal situation
749 log.info("forward packet: nextHopIP={} srcCP={} dstCP={}",
750 nextHopIp, context.inPacket().receivedFrom(), dstHost.location());
751 packetService.emit(outPacket);
752 }
753
754 /**
755 * Update intents for connectivity.
756 *
757 * ToHost: dstPrefix = dstHostIp.toIpPrefix(), nextHopIp = destHostIp
758 * ToInternet: dstPrefix = route.prefix(), nextHopIp = route.nextHopIp
Jian Lie2d87512018-08-23 17:33:05 +0900759 * returns intent submitted or not
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900760 */
761 private boolean setUpConnectivity(ConnectPoint srcCp, byte ipProto, IpPrefix srcPrefix, IpPrefix dstPrefix,
762 IpAddress nextHopIp, MacAddress treatmentSrcMac,
763 EncapsulationType encap, boolean updateMac,
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900764 boolean isDstLocalSubnet, int borderRoutePrefixLength) {
Jian Lie2d87512018-08-23 17:33:05 +0900765 if (!(simpleFabric.fabricNetwork(srcCp, VlanId.NONE) != null ||
Jian Li8df54a92018-08-23 17:01:31 +0900766 (REACTIVE_ALLOW_LINK_CP && !linkService.getIngressLinks(srcCp).isEmpty()))) {
Jian Lie2d87512018-08-23 17:33:05 +0900767 log.warn("NO REGI for srcCp not in DefaultFabricNetwork; srcCp={} srcPrefix={} dstPrefix={} nextHopIp={}",
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900768 srcCp, srcPrefix, dstPrefix, nextHopIp);
769 return false;
770 }
771
772 MacAddress nextHopMac = null;
773 ConnectPoint egressPoint = null;
774 for (Host host : hostService.getHostsByIp(nextHopIp)) {
775 if (host.mac() != null) {
776 nextHopMac = host.mac();
777 egressPoint = host.location();
778 break;
779 }
780 }
781 if (nextHopMac == null || egressPoint == null) {
782 log.info("NO REGI for unknown nextHop Cp and Mac: srcPrefix={} dstPrefix={} nextHopIp={}",
783 srcPrefix, dstPrefix, nextHopIp);
784 hostService.startMonitoringIp(nextHopIp);
785 simpleFabric.requestMac(nextHopIp);
786 return false;
787 }
788 TrafficTreatment treatment;
Jian Li8df54a92018-08-23 17:01:31 +0900789 if (updateMac && ALLOW_ETH_ADDRESS_SELECTOR) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900790 treatment = generateSetMacTreatment(nextHopMac, treatmentSrcMac);
791 } else {
792 treatment = DefaultTrafficTreatment.builder().build();
793 }
794
795 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
796 if (dstPrefix.isIp4()) {
797 selector.matchEthType(Ethernet.TYPE_IPV4);
Jian Li8df54a92018-08-23 17:01:31 +0900798 if (REACTIVE_SINGLE_TO_SINGLE && srcPrefix.prefixLength() > 0) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900799 selector.matchIPSrc(srcPrefix);
800 }
801 if (dstPrefix.prefixLength() > 0) {
802 selector.matchIPDst(dstPrefix);
803 }
Jian Li8df54a92018-08-23 17:01:31 +0900804 if (ipProto != 0 && REACTIVE_MATCH_IP_PROTO) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900805 selector.matchIPProtocol(ipProto);
806 }
807 } else {
808 selector.matchEthType(Ethernet.TYPE_IPV6);
Jian Li8df54a92018-08-23 17:01:31 +0900809 if (REACTIVE_SINGLE_TO_SINGLE && srcPrefix.prefixLength() > 0) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900810 selector.matchIPv6Src(srcPrefix);
811 }
812 if (dstPrefix.prefixLength() > 0) {
813 selector.matchIPv6Dst(dstPrefix);
814 }
Jian Li8df54a92018-08-23 17:01:31 +0900815 if (ipProto != 0 && REACTIVE_MATCH_IP_PROTO) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900816 selector.matchIPProtocol(ipProto);
817 }
818 }
819
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900820 Key key;
821 String keyProtoTag = "";
Jian Li8df54a92018-08-23 17:01:31 +0900822 if (REACTIVE_MATCH_IP_PROTO) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900823 keyProtoTag = "-p" + ipProto;
824 }
Jian Li8df54a92018-08-23 17:01:31 +0900825 if (REACTIVE_SINGLE_TO_SINGLE) {
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900826 // allocate intent per (srcPrefix, dstPrefix)
Jian Lie2d87512018-08-23 17:33:05 +0900827 key = Key.of(srcPrefix.toString() + "-to-" + dstPrefix.toString() + keyProtoTag, appId);
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900828 } else {
829 // allocate intent per (srcDeviceId, dstPrefix)
Jian Lie2d87512018-08-23 17:33:05 +0900830 key = Key.of(srcCp.deviceId().toString() + "-to-" + dstPrefix.toString() + keyProtoTag, appId);
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900831 }
832
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900833 // check and merge already existing ingress points
Ray Milkey27cff8c2018-03-05 14:58:26 -0800834 Set<FilteredConnectPoint> ingressPoints = new HashSet<>();
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900835 MultiPointToSinglePointIntent existingIntent = (MultiPointToSinglePointIntent) intentService.getIntent(key);
836 if (existingIntent != null) {
Ray Milkey27cff8c2018-03-05 14:58:26 -0800837 ingressPoints.addAll(existingIntent.filteredIngressPoints());
838 if (!ingressPoints.add(new FilteredConnectPoint(srcCp)) // alread exists and dst not changed
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900839 && egressPoint.equals(existingIntent.egressPoint())
840 && treatment.equals(existingIntent.treatment())) {
841 log.warn("srcCP is already in mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
842 srcPrefix, dstPrefix, srcCp);
843 return false;
844 }
845 log.info("update mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
846 srcPrefix, dstPrefix, srcCp);
847 } else {
848 log.info("create mp2p intent: srcPrefix={} dstPrefix={} srcCp={}",
849 srcPrefix, dstPrefix, srcCp);
Ray Milkey27cff8c2018-03-05 14:58:26 -0800850 ingressPoints.add(new FilteredConnectPoint(srcCp));
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900851 }
852
853 // priority for forwarding case
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900854 int priority = reactivePriority(true, isDstLocalSubnet, borderRoutePrefixLength);
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900855
856 MultiPointToSinglePointIntent newIntent = MultiPointToSinglePointIntent.builder()
857 .key(key)
Jian Lie2d87512018-08-23 17:33:05 +0900858 .appId(appId)
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900859 .selector(selector.build())
860 .treatment(treatment)
Ray Milkey27cff8c2018-03-05 14:58:26 -0800861 .filteredIngressPoints(ingressPoints)
862 .filteredEgressPoint(new FilteredConnectPoint(egressPoint))
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900863 .priority(priority)
864 .constraints(buildConstraints(reactiveConstraints, encap))
865 .build();
Jian Lie2d87512018-08-23 17:33:05 +0900866 log.info("submit mp2p intent: srcPrefix={} dstPrefix={} srcCp={} "
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900867 + "newIntent={} nextHopIp={} nextHopMac={} priority={}",
868 srcPrefix, dstPrefix, ingressPoints, newIntent, nextHopIp, nextHopMac, priority);
869 toBePurgedIntentKeys.remove(newIntent.key());
870 intentService.submit(newIntent);
871 return true;
872 }
873
874 // generate treatment to target
875 private TrafficTreatment generateSetMacTreatment(MacAddress dstMac, MacAddress srcMac) {
876 return DefaultTrafficTreatment.builder()
877 // NOTE: Cisco Switch requires both src and dst mac set
878 .setEthDst(dstMac)
879 .setEthSrc(srcMac)
880 .build();
881 }
882
883 // monitor border peers for routeService lookup to be effective
884 private void monitorBorderPeers() {
Jian Lie2d87512018-08-23 17:33:05 +0900885 for (FabricRoute route : simpleFabric.fabricRoutes()) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900886 hostService.startMonitoringIp(route.nextHop());
887 simpleFabric.requestMac(route.nextHop());
888 }
889 }
890
891 // priority calculator
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900892 private int reactivePriority(boolean isForward, boolean isDstLocalSubnet, int borderRoutePrefixLength) {
893 if (isDstLocalSubnet) { // -> dst:localSubnet
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900894 if (isForward) {
Jian Li8df54a92018-08-23 17:01:31 +0900895 return PRI_REACTIVE_LOCAL_FORWARD;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900896 } else { // isInterncept
Jian Li8df54a92018-08-23 17:01:31 +0900897 return PRI_REACTIVE_LOCAL_INTERCEPT;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900898 }
Lee Yongjae6dc7e4f2017-12-06 16:17:51 +0900899 } else { // -> dst:boarderRouteNextHop
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900900 int offset;
901 if (isForward) {
Jian Li8df54a92018-08-23 17:01:31 +0900902 offset = PRI_REACTIVE_BORDER_FORWARD;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900903 } else { // isIntercept
Jian Li8df54a92018-08-23 17:01:31 +0900904 offset = PRI_REACTIVE_BORDER_INTERCEPT;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900905 }
Jian Li8df54a92018-08-23 17:01:31 +0900906 return PRI_REACTIVE_BORDER_BASE
907 + borderRoutePrefixLength * PRI_REACTIVE_BORDER_STEP + offset;
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900908 }
909 }
910
911 // constraints generator
912 private List<Constraint> buildConstraints(List<Constraint> constraints, EncapsulationType encap) {
913 if (!encap.equals(EncapsulationType.NONE)) {
914 List<Constraint> newConstraints = new ArrayList<>(constraints);
915 constraints.stream()
916 .filter(c -> c instanceof EncapsulationConstraint)
917 .forEach(newConstraints::remove);
918 newConstraints.add(new EncapsulationConstraint(encap));
919 return ImmutableList.copyOf(newConstraints);
920 }
921 return constraints;
922 }
923
924 // Dump Cli Handler
925 private void dump(String subject, PrintStream out) {
Ray Milkey2ff67162018-01-22 10:14:19 -0800926 if ("intents".equals(subject)) {
Jian Lie2d87512018-08-23 17:33:05 +0900927 out.println("Routing Route Intents:\n");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900928 for (Intent entry : intentService.getIntents()) {
Jian Lie2d87512018-08-23 17:33:05 +0900929 if (appId.equals(entry.appId())) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900930 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
931 out.println(" " + intent.key().toString()
932 + " to " + intent.egressPoint().toString()
933 + " set " + intent.treatment().immediate().toString()
934 + " from " + intent.ingressPoints().toString());
935 }
936 }
937 out.println("");
938
Jian Lie2d87512018-08-23 17:33:05 +0900939 out.println("Routing Intercept Flow Rules:\n");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900940 List<FlowRule> rules = new ArrayList(interceptFlowRules);
941 Collections.sort(rules, new Comparator<FlowRule>() {
942 @Override
943 public int compare(FlowRule a, FlowRule b) {
944 int r = a.deviceId().toString().compareTo(b.deviceId().toString());
945 return (r != 0) ? r : Integer.compare(b.priority(), a.priority()); // descending on priority
946 }
947 });
948 for (FlowRule rule : rules) {
949 out.println(" device=" + rule.deviceId().toString()
950 + " priority=" + rule.priority()
951 + " selector=" + rule.selector().criteria().toString());
952 }
953 out.println("");
Jian Lie2d87512018-08-23 17:33:05 +0900954 out.println("Routing Intents to Be Purged:\n");
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900955 for (Key key: toBePurgedIntentKeys) {
956 out.println(" " + key.toString());
957 }
958 out.println("");
959
Ray Milkey2ff67162018-01-22 10:14:19 -0800960 } else if ("reactive-intents".equals(subject)) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900961 for (Intent entry : intentService.getIntents()) {
Jian Lie2d87512018-08-23 17:33:05 +0900962 if (appId.equals(entry.appId())) {
Lee Yongjae7c27bb42017-11-17 12:00:45 +0900963 MultiPointToSinglePointIntent intent = (MultiPointToSinglePointIntent) entry;
964 out.println(intent.key().toString()
965 + " to " + intent.egressPoint().toString()
966 + " set " + intent.treatment().immediate().toString()
967 + " from " + intent.ingressPoints().toString());
968 }
969 }
970 }
971 }
972
973 // Listener
974 private class InternalSimpleFabricListener implements SimpleFabricListener {
975 @Override
976 public void event(SimpleFabricEvent event) {
977 switch (event.type()) {
978 case SIMPLE_FABRIC_UPDATED:
979 refreshIntercepts();
980 refreshRouteIntents();
981 checkIntentsPurge();
982 break;
983 case SIMPLE_FABRIC_FLUSH:
984 withdrawAllReactiveIntents();
985 checkIntentsPurge();
986 break;
987 case SIMPLE_FABRIC_IDLE:
988 refreshIntercepts();
989 refreshRouteIntents();
990 checkIntentsPurge();
991 monitorBorderPeers();
992 break;
993 case SIMPLE_FABRIC_DUMP:
994 dump(event.subject(), event.out());
995 break;
996 default:
997 break;
998 }
999 }
1000 }
1001
1002}
1003