blob: 1b1cdf863a587c12b59e1eaad982bb1c5c58a8d6 [file] [log] [blame]
Jian Lia1186772018-07-27 18:06:41 +09001/*
2 * Copyright 2018-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.openstacktroubleshoot.impl;
17
Jian Li0b93b002018-07-31 13:41:08 +090018import com.google.common.collect.Sets;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onlab.packet.DeserializationException;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.ICMP;
28import org.onlab.packet.ICMPEcho;
29import org.onlab.packet.IPv4;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.IpPrefix;
Jian Lic38e9032018-08-09 17:08:38 +090032import org.onlab.packet.MacAddress;
Jian Li0b93b002018-07-31 13:41:08 +090033import org.onlab.util.KryoNamespace;
34import org.onosproject.cluster.ClusterService;
35import org.onosproject.cluster.LeadershipService;
36import org.onosproject.cluster.NodeId;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.mastership.MastershipService;
Jian Lic38e9032018-08-09 17:08:38 +090040import org.onosproject.net.DeviceId;
Jian Li0b93b002018-07-31 13:41:08 +090041import org.onosproject.net.flow.DefaultTrafficSelector;
42import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowEntry;
44import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
Jian Lib7873422018-08-18 22:34:39 +090047import org.onosproject.net.flow.criteria.Criterion;
Jian Li0b93b002018-07-31 13:41:08 +090048import org.onosproject.net.flow.criteria.IPCriterion;
49import org.onosproject.net.packet.DefaultOutboundPacket;
50import org.onosproject.net.packet.InboundPacket;
51import org.onosproject.net.packet.OutboundPacket;
52import org.onosproject.net.packet.PacketContext;
53import org.onosproject.net.packet.PacketProcessor;
54import org.onosproject.net.packet.PacketService;
55import org.onosproject.openstacknetworking.api.InstancePort;
56import org.onosproject.openstacknetworking.api.InstancePortService;
57import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
58import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
Jian Lic38e9032018-08-09 17:08:38 +090059import org.onosproject.openstacknode.api.OpenstackNode;
Jian Li0b93b002018-07-31 13:41:08 +090060import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Lia1186772018-07-27 18:06:41 +090061import org.onosproject.openstacktroubleshoot.api.OpenstackTroubleshootService;
Jian Li0b93b002018-07-31 13:41:08 +090062import org.onosproject.openstacktroubleshoot.api.Reachability;
63import org.onosproject.store.serializers.KryoNamespaces;
64import org.onosproject.store.service.AtomicCounter;
65import org.onosproject.store.service.ConsistentMap;
66import org.onosproject.store.service.Serializer;
67import org.onosproject.store.service.StorageService;
68import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
70
71import java.nio.ByteBuffer;
Jian Lic38e9032018-08-09 17:08:38 +090072import java.util.Optional;
Jian Li0b93b002018-07-31 13:41:08 +090073import java.util.Set;
74import java.util.concurrent.ExecutorService;
75import java.util.function.BooleanSupplier;
76import java.util.function.Predicate;
Jian Li0b93b002018-07-31 13:41:08 +090077
78import static com.google.common.base.Preconditions.checkNotNull;
79import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
80import static org.onlab.packet.Ethernet.TYPE_IPV4;
81import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
82import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
83import static org.onlab.util.Tools.groupedThreads;
84import static org.onosproject.net.PortNumber.TABLE;
85import static org.onosproject.net.flow.FlowEntry.FlowEntryState.ADDED;
86import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
87import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
88import static org.onosproject.openstacknetworking.api.Constants.ACL_TABLE;
Jian Lic38e9032018-08-09 17:08:38 +090089import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
Jian Li0b93b002018-07-31 13:41:08 +090090import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
91import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
Jian Lic38e9032018-08-09 17:08:38 +090092import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
Jian Li0b93b002018-07-31 13:41:08 +090093import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
94import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_PROBE_RULE;
95import static org.onosproject.openstacknetworking.api.Constants.VTAG_TABLE;
96import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
Jian Lic38e9032018-08-09 17:08:38 +090097import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Jian Li0b93b002018-07-31 13:41:08 +090098import static org.onosproject.openstacktroubleshoot.util.OpenstackTroubleshootUtil.getSegId;
Jian Lia1186772018-07-27 18:06:41 +090099
100/**
101 * Implementation of openstack troubleshoot app.
102 */
Jian Li0b93b002018-07-31 13:41:08 +0900103@Component(immediate = true)
104@Service
Jian Lia1186772018-07-27 18:06:41 +0900105public class OpenstackTroubleshootManager implements OpenstackTroubleshootService {
Jian Li0b93b002018-07-31 13:41:08 +0900106
107 private final Logger log = LoggerFactory.getLogger(getClass());
108
109 private static final int VID_TAG_RULE_INSTALL_TIMEOUT_MS = 1000;
110 private static final int ICMP_RULE_INSTALL_TIMEOUT_MS = 1000;
111 private static final int ICMP_REPLY_TIMEOUT_MS = 3000;
112 private static final String SERIALIZER_NAME = "openstack-troubleshoot";
113 private static final byte TTL = 64;
114 private static final short INITIAL_SEQ = 1;
115 private static final short MAX_ICMP_GEN = 3;
116 private static final int PREFIX_LENGTH = 32;
117 private static final int ICMP_PROCESSOR_PRIORITY = 99;
118
Jian Lic38e9032018-08-09 17:08:38 +0900119 private static final MacAddress LOCAL_MAC = MacAddress.valueOf("11:22:33:44:55:66");
120
Jian Li0b93b002018-07-31 13:41:08 +0900121 private static final String ICMP_COUNTER_NAME = "icmp-id-counter";
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected CoreService coreService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected PacketService packetService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected FlowRuleService flowRuleService;
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected StorageService storageService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected LeadershipService leadershipService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected MastershipService mastershipService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected ClusterService clusterService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected OpenstackNodeService osNodeService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected OpenstackNetworkService osNetworkService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected OpenstackFlowRuleService osFlowRuleService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected InstancePortService instancePortService;
155
156 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
157 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
158 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
159 private ConsistentMap<String, Reachability> icmpReachabilityMap;
160 private AtomicCounter icmpIdCounter;
161
162 private static final KryoNamespace SERIALIZER_DEFAULT_MAP = KryoNamespace.newBuilder()
163 .register(KryoNamespaces.API)
164 .register(Reachability.class)
165 .register(DefaultReachability.class)
166 .build();
167
168 private Set<String> icmpIds = Sets.newConcurrentHashSet();
169
170 private ApplicationId appId;
171 private NodeId localNodeId;
172
173 @Activate
174 protected void activate() {
175
176 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
177 packetService.addProcessor(packetProcessor,
178 PacketProcessor.director(ICMP_PROCESSOR_PRIORITY));
179
180 localNodeId = clusterService.getLocalNode().id();
181 leadershipService.runForLeadership(appId.name());
182
183 icmpReachabilityMap = storageService.<String, Reachability>consistentMapBuilder()
184 .withSerializer(Serializer.using(SERIALIZER_DEFAULT_MAP))
185 .withName(SERIALIZER_NAME)
186 .withApplicationId(appId)
187 .build();
188
189 icmpIdCounter = storageService.getAtomicCounter(ICMP_COUNTER_NAME);
190
191 log.info("Started");
192 }
193
194 @Deactivate
195 protected void deactivate() {
196
197 packetService.removeProcessor(packetProcessor);
198 leadershipService.withdraw(appId.name());
199 eventExecutor.shutdown();
200
201 log.info("Stopped");
202 }
203
Jian Lia1186772018-07-27 18:06:41 +0900204 @Override
Jian Lie189c1c2018-08-08 15:55:08 +0900205 public Reachability probeEastWest(InstancePort srcPort, InstancePort dstPort) {
Jian Li0b93b002018-07-31 13:41:08 +0900206
207 Reachability.Builder rBuilder = DefaultReachability.builder()
Jian Lie189c1c2018-08-08 15:55:08 +0900208 .srcIp(srcPort.ipAddress())
209 .dstIp(dstPort.ipAddress());
Jian Li0b93b002018-07-31 13:41:08 +0900210
Jian Lie189c1c2018-08-08 15:55:08 +0900211 if (srcPort.equals(dstPort)) {
Jian Li0b93b002018-07-31 13:41:08 +0900212 // self probing should always return true
213 rBuilder.isReachable(true);
214 return rBuilder.build();
215 } else {
Jian Li0b93b002018-07-31 13:41:08 +0900216 if (srcPort.state() == ACTIVE && dstPort.state() == ACTIVE) {
217
Jian Lic38e9032018-08-09 17:08:38 +0900218 // if the two ports are located in different types of networks,
219 // we immediately return unreachable state
220 if (!osNetworkService.networkType(srcPort.networkId())
221 .equals(osNetworkService.networkType(dstPort.networkId()))) {
222 rBuilder.isReachable(false);
223 return rBuilder.build();
224 }
225
Jian Li0b93b002018-07-31 13:41:08 +0900226 // install flow rules to enforce ICMP_REQUEST to be tagged and direct to ACL table
227 eventExecutor.execute(() -> setVidTagRule(srcPort, true));
228
229 // install flow rules to enforce forwarding ICMP_REPLY to controller
Jian Lic38e9032018-08-09 17:08:38 +0900230 eventExecutor.execute(() -> setEastWestIcmpReplyRule(srcPort, true));
Jian Li0b93b002018-07-31 13:41:08 +0900231
232 timeoutPredicate(1, VID_TAG_RULE_INSTALL_TIMEOUT_MS,
233 this::checkVidTagRule, srcPort.ipAddress().toString());
234
235 timeoutPredicate(1, ICMP_RULE_INSTALL_TIMEOUT_MS,
Jian Lic38e9032018-08-09 17:08:38 +0900236 this::checkEastWestIcmpReplyRule, srcPort.ipAddress().toString());
Jian Li0b93b002018-07-31 13:41:08 +0900237
238 // send out ICMP ECHO request
Jian Lic38e9032018-08-09 17:08:38 +0900239 sendIcmpEchoRequest(srcPort, dstPort, null, Direction.EAST_WEST);
Jian Li0b93b002018-07-31 13:41:08 +0900240
241 BooleanSupplier checkReachability = () -> icmpReachabilityMap.asJavaMap()
242 .values().stream().allMatch(Reachability::isReachable);
243
244 timeoutSupplier(1, ICMP_REPLY_TIMEOUT_MS, checkReachability);
245
246 // uninstall ICMP_REQUEST VID tagging rules
247 eventExecutor.execute(() -> setVidTagRule(srcPort, false));
248
249 // uninstall ICMP_REPLY enforcing rules
Jian Lic38e9032018-08-09 17:08:38 +0900250 eventExecutor.execute(() -> setEastWestIcmpReplyRule(srcPort, false));
Jian Li0b93b002018-07-31 13:41:08 +0900251
252 return icmpReachabilityMap.asJavaMap()
253 .get(String.valueOf(icmpIdCounter.get()));
254
255 } else {
256 rBuilder.isReachable(false);
257 return rBuilder.build();
258 }
259 }
260 }
261
262 @Override
Jian Lic38e9032018-08-09 17:08:38 +0900263 public Reachability probeNorthSouth(InstancePort port) {
264 Optional<OpenstackNode> gw = osNodeService.completeNodes(GATEWAY).stream().findFirst();
Jian Li0b93b002018-07-31 13:41:08 +0900265
Jian Lic38e9032018-08-09 17:08:38 +0900266 if (!gw.isPresent()) {
267 log.warn("Gateway is not available to troubleshoot north-south traffic.");
268 return null;
Jian Li0b93b002018-07-31 13:41:08 +0900269 }
270
Jian Lic38e9032018-08-09 17:08:38 +0900271 // install flow rules to enforce forwarding ICMP_REPLY to controller
272 eventExecutor.execute(() -> setNorthSouthIcmpReplyRule(port, gw.get(), true));
273
274 timeoutPredicate(1, ICMP_RULE_INSTALL_TIMEOUT_MS,
275 this::checkNorthSouthIcmpReplyRule, port.ipAddress().toString());
276
277 // send out ICMP ECHO request
278 sendIcmpEchoRequest(null, port, gw.get(), Direction.NORTH_SOUTH);
279
280 BooleanSupplier checkReachability = () -> icmpReachabilityMap.asJavaMap()
281 .values().stream().allMatch(Reachability::isReachable);
282
283 timeoutSupplier(1, ICMP_REPLY_TIMEOUT_MS, checkReachability);
284
285 // uninstall ICMP_REPLY enforcing rules
286 eventExecutor.execute(() -> setNorthSouthIcmpReplyRule(port, gw.get(), false));
287
288 return icmpReachabilityMap.asJavaMap().get(String.valueOf(icmpIdCounter.get()));
Jian Li0b93b002018-07-31 13:41:08 +0900289 }
290
291 /**
Jian Lic38e9032018-08-09 17:08:38 +0900292 * Checks whether east-west ICMP reply rule is added or not.
Jian Li0b93b002018-07-31 13:41:08 +0900293 *
Jian Lib7873422018-08-18 22:34:39 +0900294 * @param dstIp IP address
Jian Li0b93b002018-07-31 13:41:08 +0900295 * @return true if ICMP reply rule is added, false otherwise
296 */
Jian Lib7873422018-08-18 22:34:39 +0900297 private boolean checkEastWestIcmpReplyRule(String dstIp) {
298 return checkFlowRule(dstIp, IPV4_DST);
Jian Li0b93b002018-07-31 13:41:08 +0900299 }
300
301 /**
Jian Lic38e9032018-08-09 17:08:38 +0900302 * Checks whether north-south ICMP reply rule is added or not.
Jian Li0b93b002018-07-31 13:41:08 +0900303 *
Jian Lib7873422018-08-18 22:34:39 +0900304 * @param srcIp IP address
Jian Lic38e9032018-08-09 17:08:38 +0900305 * @return true if ICMP reply rule is added, false otherwise
Jian Li0b93b002018-07-31 13:41:08 +0900306 */
Jian Lib7873422018-08-18 22:34:39 +0900307 private boolean checkNorthSouthIcmpReplyRule(String srcIp) {
308 return checkFlowRule(srcIp, IPV4_SRC);
Jian Li0b93b002018-07-31 13:41:08 +0900309 }
310
311 /**
312 * Checks whether ICMP request VID tagging rule is added or not.
313 *
314 * @param srcIp source IP address
315 * @return true if the rule is added, false otherwise
316 */
317 private boolean checkVidTagRule(String srcIp) {
Jian Lib7873422018-08-18 22:34:39 +0900318 return checkFlowRule(srcIp, IPV4_SRC);
319 }
320
321 /**
322 * Checks whether flow rules with the given IP and criterion are added or not.
323 *
324 * @param ip IP address
325 * @param ipType IP criterion type (IPV4_DST or IPV4_SRC)
326 * @return true if the flow rule is added, false otherwise
327 */
328 private boolean checkFlowRule(String ip, Criterion.Type ipType) {
Jian Li0b93b002018-07-31 13:41:08 +0900329 for (FlowEntry entry : flowRuleService.getFlowEntriesById(appId)) {
330 TrafficSelector selector = entry.selector();
331
Jian Lib7873422018-08-18 22:34:39 +0900332 IPCriterion ipCriterion = (IPCriterion) selector.getCriterion(ipType);
Jian Li0b93b002018-07-31 13:41:08 +0900333
Jian Lib7873422018-08-18 22:34:39 +0900334 if (ipCriterion != null &&
335 ip.equals(ipCriterion.ip().address().toString()) &&
Jian Li0b93b002018-07-31 13:41:08 +0900336 entry.state() == ADDED) {
337 return true;
338 }
339 }
Jian Li0b93b002018-07-31 13:41:08 +0900340 return false;
341 }
342
343 /**
Jian Li0b93b002018-07-31 13:41:08 +0900344 * Installs/uninstalls a flow rule to match ingress fake ICMP request packets,
345 * and tags VNI/VID, direct the tagged packet to ACL table.
346 *
347 * @param port instance port
348 * @param install installation flag
349 */
350 private void setVidTagRule(InstancePort port, boolean install) {
351 TrafficSelector selector = DefaultTrafficSelector.builder()
352 .matchEthType(Ethernet.TYPE_IPV4)
353 .matchIPSrc(IpPrefix.valueOf(port.ipAddress(), PREFIX_LENGTH))
354 .build();
355
356 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder()
357 .setTunnelId(getSegId(osNetworkService, port))
358 .transition(ACL_TABLE);
359
360 osFlowRuleService.setRule(
361 appId,
362 port.deviceId(),
363 selector,
364 tb.build(),
365 PRIORITY_ICMP_PROBE_RULE,
366 VTAG_TABLE,
367 install);
368 }
369
370 /**
Jian Lic38e9032018-08-09 17:08:38 +0900371 * Installs/uninstalls a flow rule to match north-south ICMP reply packets,
372 * direct all ICMP reply packets to the controller.
Jian Li0b93b002018-07-31 13:41:08 +0900373 *
Jian Lic38e9032018-08-09 17:08:38 +0900374 * @param port instance port
375 * @param gw gateway node
Jian Li0b93b002018-07-31 13:41:08 +0900376 * @param install installation flag
377 */
Jian Lic38e9032018-08-09 17:08:38 +0900378 private void setNorthSouthIcmpReplyRule(InstancePort port, OpenstackNode gw,
379 boolean install) {
380 TrafficSelector selector = DefaultTrafficSelector.builder()
381 .matchEthType(Ethernet.TYPE_IPV4)
382 .matchIPSrc(IpPrefix.valueOf(port.ipAddress(), PREFIX_LENGTH))
383 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
384 .matchIcmpType(ICMP.TYPE_ECHO_REPLY)
385 .matchTunnelId(getSegId(osNetworkService, port))
386 .build();
387
388 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
389 .setIpSrc(instancePortService.floatingIp(port.portId()))
390 .setEthSrc(port.macAddress())
391 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC)
392 .punt()
393 .build();
394
395 osFlowRuleService.setRule(
396 appId,
397 gw.intgBridge(),
398 selector,
399 treatment,
400 PRIORITY_ICMP_PROBE_RULE,
401 GW_COMMON_TABLE,
402 install);
Jian Li0b93b002018-07-31 13:41:08 +0900403 }
404
405 /**
Jian Lic38e9032018-08-09 17:08:38 +0900406 * Installs/uninstalls a flow rule to match east-west ICMP reply packets,
407 * direct all ICMP reply packets to the controller.
Jian Li0b93b002018-07-31 13:41:08 +0900408 *
Jian Lic38e9032018-08-09 17:08:38 +0900409 * @param port instance port
Jian Li0b93b002018-07-31 13:41:08 +0900410 * @param install installation flag
411 */
Jian Lic38e9032018-08-09 17:08:38 +0900412 private void setEastWestIcmpReplyRule(InstancePort port, boolean install) {
Jian Li0b93b002018-07-31 13:41:08 +0900413 TrafficSelector selector = DefaultTrafficSelector.builder()
414 .matchEthType(Ethernet.TYPE_IPV4)
415 .matchIPDst(IpPrefix.valueOf(port.ipAddress(), PREFIX_LENGTH))
416 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
417 .matchIcmpType(ICMP.TYPE_ECHO_REPLY)
418 .build();
419
420 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
421 .punt()
422 .build();
423
424 osFlowRuleService.setRule(
425 appId,
426 port.deviceId(),
427 selector,
428 treatment,
429 PRIORITY_ICMP_PROBE_RULE,
430 FORWARDING_TABLE,
431 install);
432 }
433
434 /**
435 * Sends out ICMP ECHO REQUEST to destined VM.
436 *
437 * @param srcPort source instance port
438 * @param dstPort destination instance port
439 */
Jian Lic38e9032018-08-09 17:08:38 +0900440 private void sendIcmpEchoRequest(InstancePort srcPort, InstancePort dstPort,
441 OpenstackNode gateway, Direction direction) {
Jian Li0b93b002018-07-31 13:41:08 +0900442
443 short icmpSeq = INITIAL_SEQ;
444
445 short icmpId = (short) icmpIdCounter.incrementAndGet();
446
447 for (int i = 0; i < MAX_ICMP_GEN; i++) {
Jian Lic38e9032018-08-09 17:08:38 +0900448 packetService.emit(buildIcmpOutputPacket(srcPort, dstPort, gateway,
449 icmpId, icmpSeq, direction));
Jian Li0b93b002018-07-31 13:41:08 +0900450 icmpSeq++;
451 }
452 }
453
454 /**
455 * Builds ICMP Outbound packet.
456 *
457 * @param srcPort source instance port
458 * @param dstPort destination instance port
459 * @param icmpId ICMP identifier
460 * @param icmpSeq ICMP sequence number
461 */
462 private OutboundPacket buildIcmpOutputPacket(InstancePort srcPort,
463 InstancePort dstPort,
Jian Lic38e9032018-08-09 17:08:38 +0900464 OpenstackNode gateway,
Jian Li0b93b002018-07-31 13:41:08 +0900465 short icmpId,
Jian Lic38e9032018-08-09 17:08:38 +0900466 short icmpSeq,
467 Direction direction) {
Jian Li0b93b002018-07-31 13:41:08 +0900468
Jian Lic38e9032018-08-09 17:08:38 +0900469 Ethernet ethFrame;
470 IpAddress srcIp;
471 IpAddress dstIp;
472 DeviceId deviceId;
473
474 if (direction == Direction.EAST_WEST) {
475 ethFrame = constructEastWestIcmpPacket(srcPort, dstPort, icmpId, icmpSeq);
476 srcIp = srcPort.ipAddress();
477 dstIp = dstPort.ipAddress();
478 deviceId = srcPort.deviceId();
479 } else if (direction == Direction.NORTH_SOUTH) {
480 ethFrame = constructNorthSouthIcmpPacket(dstPort, icmpId, icmpSeq);
481 srcIp = clusterService.getLocalNode().ip();
482 dstIp = instancePortService.floatingIp(dstPort.portId());
483 deviceId = gateway.intgBridge();
484 } else {
485 log.warn("Invalid traffic direction {}", direction);
486 return null;
487 }
Jian Li0b93b002018-07-31 13:41:08 +0900488
489 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
490
491 // we send out the packet to ingress table (index is 0) of source OVS
492 // to enforce the Outbound packet to go through the ingress and egress
493 // pipeline
494 tBuilder.setOutput(TABLE);
495
496 Reachability reachability = DefaultReachability.builder()
Jian Lic38e9032018-08-09 17:08:38 +0900497 .srcIp(srcIp)
498 .dstIp(dstIp)
Jian Li0b93b002018-07-31 13:41:08 +0900499 .isReachable(false)
500 .build();
501
502 icmpReachabilityMap.put(String.valueOf(icmpId), reachability);
503 icmpIds.add(String.valueOf(icmpId));
504
505 return new DefaultOutboundPacket(
Jian Lic38e9032018-08-09 17:08:38 +0900506 deviceId,
Jian Li0b93b002018-07-31 13:41:08 +0900507 tBuilder.build(),
508 ByteBuffer.wrap(ethFrame.serialize()));
509 }
510
511 /**
512 * Constructs an ICMP packet with given source and destination IP/MAC.
513 *
Jian Lic38e9032018-08-09 17:08:38 +0900514 * @param srcIp source IP address
515 * @param dstIp destination IP address
516 * @param srcMac source MAC address
517 * @param dstMac destination MAC address
Jian Li0b93b002018-07-31 13:41:08 +0900518 * @param icmpId ICMP identifier
519 * @param icmpSeq ICMP sequence number
520 * @return an ethernet frame which contains ICMP payload
521 */
Jian Lic38e9032018-08-09 17:08:38 +0900522 private Ethernet constructIcmpPacket(IpAddress srcIp, IpAddress dstIp,
523 MacAddress srcMac, MacAddress dstMac,
Jian Li0b93b002018-07-31 13:41:08 +0900524 short icmpId, short icmpSeq) {
Jian Lic38e9032018-08-09 17:08:38 +0900525
Jian Li0b93b002018-07-31 13:41:08 +0900526 // Ethernet frame
527 Ethernet ethFrame = new Ethernet();
528
529 ethFrame.setEtherType(TYPE_IPV4);
Jian Lic38e9032018-08-09 17:08:38 +0900530 ethFrame.setSourceMACAddress(srcMac);
531 ethFrame.setDestinationMACAddress(dstMac);
Jian Li0b93b002018-07-31 13:41:08 +0900532
533 // IP packet
534 IPv4 iPacket = new IPv4();
Jian Lic38e9032018-08-09 17:08:38 +0900535 iPacket.setDestinationAddress(dstIp.toString());
536 iPacket.setSourceAddress(srcIp.toString());
Jian Li0b93b002018-07-31 13:41:08 +0900537 iPacket.setTtl(TTL);
538 iPacket.setProtocol(IPv4.PROTOCOL_ICMP);
539
540 // ICMP packet
541 ICMP icmp = new ICMP();
542 icmp.setIcmpType(TYPE_ECHO_REQUEST)
543 .setIcmpCode(TYPE_ECHO_REQUEST)
544 .resetChecksum();
545
546 // ICMP ECHO packet
547 ICMPEcho icmpEcho = new ICMPEcho();
548 icmpEcho.setIdentifier(icmpId)
549 .setSequenceNum(icmpSeq);
550
551 ByteBuffer byteBufferIcmpEcho = ByteBuffer.wrap(icmpEcho.serialize());
552
553 try {
554 icmp.setPayload(ICMPEcho.deserializer().deserialize(byteBufferIcmpEcho.array(),
555 0, ICMPEcho.ICMP_ECHO_HEADER_LENGTH));
556 } catch (DeserializationException e) {
557 log.warn("Failed to deserialize ICMP ECHO REQUEST packet");
558 }
559
560 ByteBuffer byteBufferIcmp = ByteBuffer.wrap(icmp.serialize());
561
562 try {
563 iPacket.setPayload(ICMP.deserializer().deserialize(byteBufferIcmp.array(),
564 0,
565 byteBufferIcmp.array().length));
566 } catch (DeserializationException e) {
567 log.warn("Failed to deserialize ICMP packet");
568 }
569
570 ethFrame.setPayload(iPacket);
571
572 return ethFrame;
573 }
574
575 /**
Jian Lic38e9032018-08-09 17:08:38 +0900576 * Constructs an east-west ICMP packet with given source and destination IP/MAC.
577 *
578 * @param srcPort source instance port
579 * @param dstPort destination instance port
580 * @param icmpId ICMP identifier
581 * @param icmpSeq ICMP sequence number
582 * @return an ethernet frame which contains ICMP payload
583 */
584 private Ethernet constructEastWestIcmpPacket(InstancePort srcPort,
585 InstancePort dstPort,
586 short icmpId, short icmpSeq) {
587 boolean isRemote = true;
588
589 if (srcPort.deviceId().equals(dstPort.deviceId()) &&
590 osNetworkService.gatewayIp(srcPort.portId())
591 .equals(osNetworkService.gatewayIp(dstPort.portId()))) {
592 isRemote = false;
593 }
594
595 // if the source and destination VMs are located in different OVS,
596 // we will assign fake gateway MAC as the destination MAC
597 MacAddress dstMac = isRemote ? DEFAULT_GATEWAY_MAC : dstPort.macAddress();
598
599 return constructIcmpPacket(srcPort.ipAddress(), dstPort.ipAddress(),
600 srcPort.macAddress(), dstMac, icmpId, icmpSeq);
601 }
602
603 /**
604 * Constructs a north-south ICMP packet with the given destination IP/MAC.
605 *
606 * @param dstPort destination instance port
607 * @param icmpId ICMP identifier
608 * @param icmpSeq ICMP sequence number
609 * @return an ethernet frame which contains ICMP payload
610 */
611 private Ethernet constructNorthSouthIcmpPacket(InstancePort dstPort,
612 short icmpId, short icmpSeq) {
613
614 IpAddress localIp = clusterService.getLocalNode().ip();
615 IpAddress fip = instancePortService.floatingIp(dstPort.portId());
616
617 if (fip != null) {
618 return constructIcmpPacket(localIp, fip, LOCAL_MAC, dstPort.macAddress(),
619 icmpId, icmpSeq);
620 } else {
621 return null;
622 }
623 }
624
625 /**
Jian Li0b93b002018-07-31 13:41:08 +0900626 * Handles ICMP ECHO REPLY packets.
627 *
628 * @param ipPacket IP packet
629 * @param icmp ICMP packet
630 */
631 private void handleIcmpEchoReply(IPv4 ipPacket, ICMP icmp) {
632
633 String icmpKey = icmpId(icmp);
634
635 String srcIp = IPv4.fromIPv4Address(ipPacket.getDestinationAddress());
636 String dstIp = IPv4.fromIPv4Address(ipPacket.getSourceAddress());
637
638 Reachability reachability = DefaultReachability.builder()
639 .srcIp(IpAddress.valueOf(srcIp))
640 .dstIp(IpAddress.valueOf(dstIp))
641 .isReachable(false)
642 .build();
643
644 icmpReachabilityMap.computeIfPresent(icmpKey, (key, value) -> {
645 if (value.equals(reachability)) {
646
647 log.debug("src: {}, dst: {} is reachable!", value.dstIp(), value.srcIp());
648
649 return DefaultReachability.builder()
650 .srcIp(IpAddress.valueOf(srcIp))
651 .dstIp(IpAddress.valueOf(dstIp))
652 .isReachable(true)
653 .build();
654 }
655 return reachability;
656 });
657 }
658
659 /**
660 * Obtains an unique ICMP key.
661 *
662 * @param icmp ICMP packet
663 * @return ICMP key
664 */
665 private String icmpId(ICMP icmp) {
666 ICMPEcho echo = (ICMPEcho) icmp.getPayload();
667 checkNotNull(echo);
668
669 short icmpId = echo.getIdentifier();
670
671 return String.valueOf(icmpId);
672 }
673
674 /**
675 * Holds the current thread unit the timeout expires, during the hold the
676 * thread periodically execute the given method.
677 *
678 * @param count count of unit
679 * @param unit unit
680 * @param predicate predicate
681 * @param predicateArg predicate argument
682 */
683 private void timeoutPredicate(long count, int unit,
684 Predicate<String> predicate, String predicateArg) {
685 long timeoutExpiredMs = System.currentTimeMillis() + unit * count;
686
687 while (true) {
688
689 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
690
691 if (predicate.test(predicateArg)) {
692 break;
693 }
694
695 if (waitMs <= 0) {
696 break;
697 }
698 }
699 }
700
701 /**
702 * Holds the current thread unit the timeout expires, during the hold the
703 * thread periodically execute the given method.
704 *
705 * @param count count of unit
706 * @param unit unit
707 * @param supplier boolean supplier
708 */
709 private void timeoutSupplier(long count, int unit, BooleanSupplier supplier) {
710 long timeoutExpiredMs = System.currentTimeMillis() + unit * count;
711
712 while (true) {
713
714 long waitMs = timeoutExpiredMs - System.currentTimeMillis();
715
716 if (supplier.getAsBoolean()) {
717 break;
718 }
719
720 if (waitMs <= 0) {
721 break;
722 }
723 }
724 }
725
726 private class InternalPacketProcessor implements PacketProcessor {
727
728 @Override
729 public void process(PacketContext context) {
730 if (context.isHandled()) {
731 return;
732 }
733
734 InboundPacket pkt = context.inPacket();
735 Ethernet ethernet = pkt.parsed();
736 if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
737 return;
738 }
739
740 IPv4 iPacket = (IPv4) ethernet.getPayload();
741 if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
742 eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
743 }
744 }
745
746 /**
747 * Processes the received ICMP packet.
748 *
749 * @param context packet context
750 * @param ethernet ethernet
751 */
752 private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
753 IPv4 ipPacket = (IPv4) ethernet.getPayload();
754 ICMP icmp = (ICMP) ipPacket.getPayload();
755 log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
756 "dest MAC:{}, dest IP:{}",
757 ethernet.getSourceMAC(),
758 IpAddress.valueOf(ipPacket.getSourceAddress()),
759 ethernet.getDestinationMAC(),
760 IpAddress.valueOf(ipPacket.getDestinationAddress()));
761
762 String icmpId = icmpId(icmp);
763
764 // if the ICMP ID is not contained in ICMP ID set, we do not handle it
765 if (!icmpIds.contains(icmpId)) {
766 return;
767 }
768
769 switch (icmp.getIcmpType()) {
770 case TYPE_ECHO_REPLY:
771 handleIcmpEchoReply(ipPacket, icmp);
772 context.block();
773 icmpIds.remove(icmpId);
774 break;
775 default:
776 break;
777 }
778 }
Jian Lia1186772018-07-27 18:06:41 +0900779 }
780}