blob: 02a6579c3b9f379235ff31ad6f024d1be8b1fd9a [file] [log] [blame]
Jian Li4f368e82018-07-02 14:22:22 +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.openstackvtap.impl;
17
Jian Li614cb092018-07-03 22:41:42 +090018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Lists;
Jian Li26ef1302018-07-04 14:37:06 +090021import com.google.common.collect.Sets;
Jian Li614cb092018-07-03 22:41:42 +090022import org.apache.felix.scr.annotations.Activate;
Jian Li19f25262018-07-03 22:37:12 +090023import org.apache.felix.scr.annotations.Component;
Jian Li614cb092018-07-03 22:41:42 +090024import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Jian Li19f25262018-07-03 22:37:12 +090027import org.apache.felix.scr.annotations.Service;
Jian Li26ef1302018-07-04 14:37:06 +090028import org.onlab.packet.IpAddress;
Jian Li614cb092018-07-03 22:41:42 +090029import org.onlab.packet.IpPrefix;
Jian Li19f25262018-07-03 22:37:12 +090030import org.onlab.packet.VlanId;
Jian Li614cb092018-07-03 22:41:42 +090031import org.onosproject.cluster.ClusterService;
32import org.onosproject.cluster.LeadershipService;
33import org.onosproject.cluster.NodeId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
36import org.onosproject.core.GroupId;
Jian Li19f25262018-07-03 22:37:12 +090037import org.onosproject.event.AbstractListenerManager;
Jian Li614cb092018-07-03 22:41:42 +090038import org.onosproject.mastership.MastershipService;
Jian Li26ef1302018-07-04 14:37:06 +090039import org.onosproject.net.Device;
Jian Li38e4d942018-07-03 22:19:16 +090040import org.onosproject.net.DeviceId;
Jian Li614cb092018-07-03 22:41:42 +090041import org.onosproject.net.Host;
42import org.onosproject.net.HostLocation;
Jian Li19f25262018-07-03 22:37:12 +090043import org.onosproject.net.PortNumber;
Jian Li614cb092018-07-03 22:41:42 +090044import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
45import org.onosproject.net.device.DeviceEvent;
46import org.onosproject.net.device.DeviceListener;
47import org.onosproject.net.device.DeviceService;
48import org.onosproject.net.driver.DefaultDriverData;
49import org.onosproject.net.driver.DefaultDriverHandler;
50import org.onosproject.net.driver.Driver;
51import org.onosproject.net.driver.DriverHandler;
52import org.onosproject.net.driver.DriverService;
53import org.onosproject.net.flow.DefaultFlowRule;
54import org.onosproject.net.flow.DefaultTrafficSelector;
55import org.onosproject.net.flow.DefaultTrafficTreatment;
56import org.onosproject.net.flow.FlowRule;
57import org.onosproject.net.flow.FlowRuleOperations;
58import org.onosproject.net.flow.FlowRuleOperationsContext;
59import org.onosproject.net.flow.FlowRuleService;
60import org.onosproject.net.flow.TrafficSelector;
61import org.onosproject.net.flow.TrafficTreatment;
62import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li614cb092018-07-03 22:41:42 +090063import org.onosproject.net.group.DefaultGroupBucket;
64import org.onosproject.net.group.DefaultGroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090065import org.onosproject.net.group.GroupBucket;
66import org.onosproject.net.group.GroupBuckets;
67import org.onosproject.net.group.GroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090068import org.onosproject.net.group.GroupService;
69import org.onosproject.net.host.HostEvent;
70import org.onosproject.net.host.HostListener;
71import org.onosproject.net.host.HostService;
Jian Li26ef1302018-07-04 14:37:06 +090072import org.onosproject.openstacknode.api.OpenstackNodeEvent;
73import org.onosproject.openstacknode.api.OpenstackNodeListener;
74import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li38e4d942018-07-03 22:19:16 +090075import org.onosproject.openstackvtap.api.OpenstackVtap;
Jian Li26ef1302018-07-04 14:37:06 +090076import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
Jian Li19f25262018-07-03 22:37:12 +090077import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
78import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
79import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
Jian Li26ef1302018-07-04 14:37:06 +090080import org.onosproject.openstackvtap.api.OpenstackVtapId;
Jian Li38e4d942018-07-03 22:19:16 +090081import org.onosproject.openstackvtap.api.OpenstackVtapListener;
Jian Li4f368e82018-07-02 14:22:22 +090082import org.onosproject.openstackvtap.api.OpenstackVtapService;
Jian Li614cb092018-07-03 22:41:42 +090083import org.onosproject.openstackvtap.api.OpenstackVtapStore;
84import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
Jian Li614cb092018-07-03 22:41:42 +090085import org.osgi.service.component.ComponentContext;
86import org.slf4j.Logger;
Jian Li4f368e82018-07-02 14:22:22 +090087
Jian Li614cb092018-07-03 22:41:42 +090088import java.util.List;
89import java.util.Objects;
Jian Li38e4d942018-07-03 22:19:16 +090090import java.util.Set;
Jian Li614cb092018-07-03 22:41:42 +090091import java.util.concurrent.ScheduledExecutorService;
92import java.util.function.BiFunction;
93import java.util.stream.Collectors;
94import java.util.stream.StreamSupport;
95
96import static com.google.common.base.Preconditions.checkNotNull;
97import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
98import static org.onlab.packet.Ethernet.TYPE_IPV4;
99import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
100import static org.onlab.packet.IPv4.PROTOCOL_TCP;
101import static org.onlab.packet.IPv4.PROTOCOL_UDP;
Jian Li26ef1302018-07-04 14:37:06 +0900102import static org.onlab.packet.VlanId.UNTAGGED;
Jian Li614cb092018-07-03 22:41:42 +0900103import static org.onlab.util.Tools.groupedThreads;
Jian Li26ef1302018-07-04 14:37:06 +0900104import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
Jian Li614cb092018-07-03 22:41:42 +0900105import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
106import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
107import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
108import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_GROUP_TABLE;
109import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_MIRROR_TABLE;
110import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_TABLE;
111import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_GROUP_TABLE;
112import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_MIRROR_TABLE;
113import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TABLE;
114import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE;
115import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE;
116import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
Jian Li26ef1302018-07-04 14:37:06 +0900117import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
118import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey;
Jian Li614cb092018-07-03 22:41:42 +0900119import static org.slf4j.LoggerFactory.getLogger;
Jian Li38e4d942018-07-03 22:19:16 +0900120
Jian Li4f368e82018-07-02 14:22:22 +0900121/**
Jian Li19f25262018-07-03 22:37:12 +0900122 * Provides basic implementation of the user APIs.
Jian Li4f368e82018-07-02 14:22:22 +0900123 */
Jian Li19f25262018-07-03 22:37:12 +0900124@Component(immediate = true)
125@Service
126public class OpenstackVtapManager
127 extends AbstractListenerManager<OpenstackVtapEvent, OpenstackVtapListener>
128 implements OpenstackVtapService, OpenstackVtapAdminService {
Jian Li4f368e82018-07-02 14:22:22 +0900129
Jian Li614cb092018-07-03 22:41:42 +0900130 private final Logger log = getLogger(getClass());
131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected CoreService coreService;
134
135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected ClusterService clusterService;
137
138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected LeadershipService leadershipService;
140
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected MastershipService mastershipService;
143
144 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
145 protected DriverService driverService;
146
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected FlowRuleService flowRuleService;
149
150 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
151 protected GroupService groupService;
152
153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
154 protected DeviceService deviceService;
155
156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
157 protected HostService hostService;
158
159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
160 protected OpenstackVtapStore store;
161
Jian Li26ef1302018-07-04 14:37:06 +0900162 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
163 protected OpenstackNodeService osNodeService;
164
Jian Li614cb092018-07-03 22:41:42 +0900165 public static final String APP_ID = "org.onosproject.openstackvtap";
166
167 public static final String VTAP_ID_NULL = "OpenstackVtap ID cannot be null";
168 public static final String VTAP_DESC_NULL = "OpenstackVtap fields cannot be null";
169 public static final String DEVICE_ID_NULL = "Device ID cannot be null";
170
171 private static final int PRIORITY_VTAP_RULE = 50000;
172 private static final int PRIORITY_VTAP_OUTPORT_RULE = 1000;
173 private static final int PRIORITY_VTAP_DROP = 0;
174
175 private static final int NONE_TABLE = -1;
176 private static final int INBOUND_NEXT_TABLE = DHCP_ARP_TABLE;
177 private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
178 private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
179
Jian Li311a9c92018-07-09 16:48:36 +0900180 private static final IpPrefix ARBITRARY_IP_PREFIX =
181 IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0);
182 private static final String TABLE_PROPERTY_KEY = "table";
183
Jian Li614cb092018-07-03 22:41:42 +0900184 private final DeviceListener deviceListener = new InternalDeviceListener();
185 private final HostListener hostListener = new InternalHostListener();
Jian Li26ef1302018-07-04 14:37:06 +0900186 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
Jian Li614cb092018-07-03 22:41:42 +0900187
188 private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
189
190 private ApplicationId appId;
191 private NodeId localNodeId;
192 private ScheduledExecutorService eventExecutor;
193
194
195 @Activate
196 public void activate(ComponentContext context) {
197 appId = coreService.registerApplication(APP_ID);
198 localNodeId = clusterService.getLocalNode().id();
199 leadershipService.runForLeadership(appId.name());
200
201 eventExecutor = newSingleThreadScheduledExecutor(
202 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
203
204 store.setDelegate(delegate);
205 eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
206
207 deviceService.addListener(deviceListener);
208 hostService.addListener(hostListener);
Jian Li26ef1302018-07-04 14:37:06 +0900209 osNodeService.addListener(osNodeListener);
210
Jian Lib1ca1a22018-07-06 13:31:39 +0900211 initFlowAndGroupForCompNodes();
Jian Li614cb092018-07-03 22:41:42 +0900212
213 log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900214 }
215
Jian Li614cb092018-07-03 22:41:42 +0900216 @Deactivate
217 public void deactivate() {
Jian Lib1ca1a22018-07-06 13:31:39 +0900218 clearFlowAndGroupForCompNodes();
219
Jian Li26ef1302018-07-04 14:37:06 +0900220 osNodeService.removeListener(osNodeListener);
Jian Li614cb092018-07-03 22:41:42 +0900221 hostService.removeListener(hostListener);
222 deviceService.removeListener(deviceListener);
Jian Li19f25262018-07-03 22:37:12 +0900223
Jian Li614cb092018-07-03 22:41:42 +0900224 eventDispatcher.removeSink(OpenstackVtapEvent.class);
225 store.unsetDelegate(delegate);
Jian Li19f25262018-07-03 22:37:12 +0900226
Jian Li614cb092018-07-03 22:41:42 +0900227 eventExecutor.shutdown();
228 leadershipService.withdraw(appId.name());
Jian Li19f25262018-07-03 22:37:12 +0900229
Jian Li614cb092018-07-03 22:41:42 +0900230 log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900231 }
232
233 @Override
234 public int getVtapCount(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900235 return store.getVtapCount(type);
Jian Li38e4d942018-07-03 22:19:16 +0900236 }
237
238 @Override
Jian Li19f25262018-07-03 22:37:12 +0900239 public Set<OpenstackVtap> getVtaps(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900240 return store.getVtaps(type);
Jian Li38e4d942018-07-03 22:19:16 +0900241 }
242
243 @Override
244 public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
Jian Li614cb092018-07-03 22:41:42 +0900245 checkNotNull(vTapId, VTAP_ID_NULL);
246 return store.getVtap(vTapId);
Jian Li38e4d942018-07-03 22:19:16 +0900247 }
248
249 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900250 public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
251 DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900252 checkNotNull(deviceId, DEVICE_ID_NULL);
253 return store.getVtapsByDeviceId(type, deviceId);
254 }
255
Jian Li614cb092018-07-03 22:41:42 +0900256 @Override
Jian Li311a9c92018-07-09 16:48:36 +0900257 public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterion) {
258 checkNotNull(vTapCriterion, VTAP_DESC_NULL);
Jian Li614cb092018-07-03 22:41:42 +0900259
260 Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
Jian Li311a9c92018-07-09 16:48:36 +0900261 getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900262 Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
Jian Li311a9c92018-07-09 16:48:36 +0900263 getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900264
Jian Li26ef1302018-07-04 14:37:06 +0900265 OpenstackVtap description =
266 DefaultOpenstackVtap.builder()
267 .id(OpenstackVtapId.vTapId())
268 .type(type)
Jian Li311a9c92018-07-09 16:48:36 +0900269 .vTapCriterion(vTapCriterion)
Jian Li26ef1302018-07-04 14:37:06 +0900270 .txDeviceIds(txDevices)
271 .rxDeviceIds(rxDevices)
272 .build();
Jian Li614cb092018-07-03 22:41:42 +0900273 return store.createOrUpdateVtap(description.id(), description, true);
274 }
275
276 @Override
277 public OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap) {
278 checkNotNull(vTapId, VTAP_ID_NULL);
279 checkNotNull(vTap, VTAP_DESC_NULL);
280
281 if (store.getVtap(vTapId) == null) {
282 return null;
283 }
284
285 Set<DeviceId> txDevices = vTap.type().isValid(Type.VTAP_TX) ?
286 getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
287 Set<DeviceId> rxDevices = vTap.type().isValid(Type.VTAP_RX) ?
288 getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
289
Jian Li26ef1302018-07-04 14:37:06 +0900290 DefaultOpenstackVtap description =
291 DefaultOpenstackVtap.builder()
292 .id(vTapId)
293 .type(vTap.type())
294 .vTapCriterion(vTap.vTapCriterion())
295 .txDeviceIds(txDevices)
296 .rxDeviceIds(rxDevices)
297 .build();
Jian Li614cb092018-07-03 22:41:42 +0900298 return store.createOrUpdateVtap(vTapId, description, true);
299 }
300
301 @Override
302 public OpenstackVtap removeVtap(OpenstackVtapId vTapId) {
303 checkNotNull(vTapId, VTAP_ID_NULL);
304 return store.removeVtapById(vTapId);
305 }
306
307 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900308 public void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type,
309 PortNumber portNumber, VlanId vlanId) {
310
Jian Li614cb092018-07-03 22:41:42 +0900311 // Make output table
312 if (type.isValid(Type.VTAP_TX)) {
313 createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, portNumber, vlanId);
314 }
Jian Li26ef1302018-07-04 14:37:06 +0900315
Jian Li614cb092018-07-03 22:41:42 +0900316 if (type.isValid(Type.VTAP_RX)) {
317 createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
318 createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
319 }
320 }
321
322 @Override
323 public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, int vni) {
324 // TODO: need to provide implementation
325 }
326
Jian Li26ef1302018-07-04 14:37:06 +0900327 /**
328 * Obtains the identifier set of edge device where the targeted host is located.
329 * Note that, in most of cases target host is attached to one device,
330 * however, in some cases, the host can be attached to multiple devices.
331 *
332 * @param type vTap type
333 * @param criterion vTap criterion
334 * @return a collection of device identifiers
335 */
336 private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion criterion) {
337 Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
338 StreamSupport.stream(hostService.getHosts().spliterator(), true)
339 .forEach(host -> {
340 if (host.ipAddresses().stream()
341 .anyMatch(ip -> containsIp(type, criterion, ip))) {
342 deviceIds.addAll(host.locations().stream()
343 .map(HostLocation::deviceId)
344 .collect(Collectors.toSet()));
345 }
346 });
347 return deviceIds;
Jian Li614cb092018-07-03 22:41:42 +0900348 }
349
Jian Li26ef1302018-07-04 14:37:06 +0900350 /**
351 * Checks whether the given IP address is included in vTap criterion.
352 * We both check the TX and RX directions.
353 *
354 * @param type vTap type
355 * @param criterion vTap criterion
356 * @param ip IP address
357 * @return boolean value indicates the check result
358 */
359 private boolean containsIp(Type type, OpenstackVtapCriterion criterion, IpAddress ip) {
360 boolean isTxEdge = type.isValid(Type.VTAP_TX) &&
361 criterion.srcIpPrefix().contains(ip);
362 boolean isRxEdge = type.isValid(Type.VTAP_RX) &&
363 criterion.dstIpPrefix().contains(ip);
364
365 return isTxEdge || isRxEdge;
Jian Li614cb092018-07-03 22:41:42 +0900366 }
367
Jian Li26ef1302018-07-04 14:37:06 +0900368 /**
369 * Updates device list of vTaps with respect to the host changes.
370 *
371 * @param newHost new host instance
372 * @param oldHost old host instance
373 */
Jian Li614cb092018-07-03 22:41:42 +0900374 private void updateHost(Host newHost, Host oldHost) {
375 // update devices for vTap tx
376 getVtaps(Type.VTAP_TX).parallelStream().forEach(vTap -> {
Jian Li26ef1302018-07-04 14:37:06 +0900377
378 if (hostDiff(oldHost, newHost, vTap.vTapCriterion().srcIpPrefix())) {
379 oldHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900380 .forEach(deviceId ->
Jian Li26ef1302018-07-04 14:37:06 +0900381 store.removeDeviceFromVtap(vTap.id(), Type.VTAP_TX,
382 oldHost.location().deviceId()));
Jian Li614cb092018-07-03 22:41:42 +0900383 }
Jian Li26ef1302018-07-04 14:37:06 +0900384
385 if (hostDiff(newHost, oldHost, vTap.vTapCriterion().srcIpPrefix())) {
386 newHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900387 .forEach(deviceId ->
388 store.addDeviceToVtap(vTap.id(), Type.VTAP_TX,
389 newHost.location().deviceId()));
390 }
391 });
392
393 // update devices for vTap rx
394 getVtaps(Type.VTAP_RX).parallelStream().forEach(vTap -> {
Jian Li26ef1302018-07-04 14:37:06 +0900395
396 if (hostDiff(oldHost, newHost, vTap.vTapCriterion().dstIpPrefix())) {
397 oldHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900398 .forEach(deviceId ->
399 store.removeDeviceFromVtap(vTap.id(), Type.VTAP_RX,
400 oldHost.location().deviceId()));
401 }
Jian Li26ef1302018-07-04 14:37:06 +0900402
403 if (hostDiff(newHost, oldHost, vTap.vTapCriterion().dstIpPrefix())) {
404 newHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900405 .forEach(deviceId ->
406 store.addDeviceToVtap(vTap.id(), Type.VTAP_RX,
407 newHost.location().deviceId()));
408 }
409 });
410 }
411
Jian Li26ef1302018-07-04 14:37:06 +0900412 /**
413 * Checks whether the given IP prefix is contained in the first host rather
414 * than in the second host.
415 *
416 * @param host1 first host instance
417 * @param host2 second host instance
418 * @param ipPrefix IP prefix to be looked up
419 * @return boolean value
420 */
421 private boolean hostDiff(Host host1, Host host2, IpPrefix ipPrefix) {
422 return ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) &&
423 (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains)));
Jian Li614cb092018-07-03 22:41:42 +0900424 }
425
Jian Li26ef1302018-07-04 14:37:06 +0900426 /**
Jian Lib1ca1a22018-07-06 13:31:39 +0900427 * Initializes the flow rules and group tables for all completed compute nodes.
428 */
429 private void initFlowAndGroupForCompNodes() {
430 osNodeService.completeNodes(COMPUTE).forEach(node ->
431 initFlowAndGroupByDeviceId(node.intgBridge()));
432 }
433
434 /**
Jian Li26ef1302018-07-04 14:37:06 +0900435 * Initializes the flow rules and group table of the given device identifier.
436 *
437 * @param deviceId device identifier
438 */
439 private void initFlowAndGroupByDeviceId(DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900440 // Make vTap pipeline
441 // TODO: need to selective creation by store device consistentMap
Jian Li26ef1302018-07-04 14:37:06 +0900442 initVtapPipeline(deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900443
444 // Install tx filter
445 getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> {
446 connectTables(deviceId,
447 VTAP_INBOUND_TABLE, NONE_TABLE, VTAP_INBOUND_GROUP_TABLE,
448 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
449 });
450
451 // Install rx filter
452 getVtapsByDeviceId(Type.VTAP_RX, deviceId).forEach(vTap -> {
453 connectTables(deviceId,
454 VTAP_FLAT_OUTBOUND_TABLE, NONE_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
455 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
456 connectTables(deviceId,
457 VTAP_OUTBOUND_TABLE, NONE_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
458 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
459 });
460 }
461
Jian Li26ef1302018-07-04 14:37:06 +0900462 /**
463 * Initializes vTap pipeline of the given device.
464 *
465 * @param deviceId device identifier
466 */
467 private void initVtapPipeline(DeviceId deviceId) {
468 // Make output table
469 createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, null, null);
470 createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, null, null);
471 createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, null, null);
472
473 // Make tx group table
474 createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE,
475 ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE),
476 ImmutableList.of());
477
478 // Make rx group table
479 createGroupTable(deviceId, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
480 ImmutableList.of(FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE),
481 ImmutableList.of());
482 createGroupTable(deviceId, VTAP_OUTBOUND_GROUP_TABLE,
483 ImmutableList.of(OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE),
484 ImmutableList.of());
485 }
486
487 /**
Jian Lib1ca1a22018-07-06 13:31:39 +0900488 * Purges all flow rules and group tables for completed compute nodes.
489 */
490 private void clearFlowAndGroupForCompNodes() {
491 osNodeService.completeNodes(COMPUTE).forEach(node ->
492 clearFlowAndGroupByDeviceId(node.intgBridge()));
493 }
494
495 /**
Jian Li26ef1302018-07-04 14:37:06 +0900496 * Purges all flow rules and group tables using the given device identifier.
497 *
498 * @param deviceId device identifier
499 */
Jian Lib1ca1a22018-07-06 13:31:39 +0900500 private void clearFlowAndGroupByDeviceId(DeviceId deviceId) {
Jian Li26ef1302018-07-04 14:37:06 +0900501 Set<FlowRule> purgedRules = Sets.newConcurrentHashSet();
502 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
503 if (flowRule.deviceId().equals(deviceId)) {
504 purgedRules.add(flowRule);
505 }
506 }
507
Jian Libd295cd2018-07-22 11:53:57 +0900508 flowRuleService.removeFlowRules(purgedRules.toArray(new FlowRule[0]));
Jian Li26ef1302018-07-04 14:37:06 +0900509
510 groupService.getGroups(deviceId, appId).forEach(group -> {
511 groupService.removeGroup(deviceId, group.appCookie(), appId);
Jian Li614cb092018-07-03 22:41:42 +0900512 });
Jian Li26ef1302018-07-04 14:37:06 +0900513 log.info("OpenstackVtap flow rules and groups are purged");
Jian Li614cb092018-07-03 22:41:42 +0900514 }
515
516 private void installFilterRule(Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
Jian Li26ef1302018-07-04 14:37:06 +0900517 OpenstackVtapCriterion vTapCriterion, boolean install) {
Jian Li614cb092018-07-03 22:41:42 +0900518 final int inbound = 0;
519 final int flatOutbound = 1;
520 final int outbound = 2;
521
522 BiFunction<Set<DeviceId>, Integer, Void> installFlow = (deviceIds, table) -> {
523 int inTable = (table == inbound ? VTAP_INBOUND_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900524 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_TABLE :
525 VTAP_OUTBOUND_TABLE));
526
Jian Li614cb092018-07-03 22:41:42 +0900527 int outGroup = (table == inbound ? VTAP_INBOUND_GROUP_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900528 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_GROUP_TABLE :
529 VTAP_OUTBOUND_GROUP_TABLE));
530
Jian Li614cb092018-07-03 22:41:42 +0900531 deviceIds.stream()
532 .filter(deviceId -> mastershipService.isLocalMaster(deviceId))
533 .forEach(deviceId -> {
Jian Li26ef1302018-07-04 14:37:06 +0900534 connectTables(deviceId, inTable, NONE_TABLE, outGroup,
535 vTapCriterion, PRIORITY_VTAP_RULE, install);
Jian Li614cb092018-07-03 22:41:42 +0900536 });
537 return null;
538 };
539
540 installFlow.apply(txDeviceIds, inbound);
541 installFlow.apply(rxDeviceIds, flatOutbound);
542 installFlow.apply(rxDeviceIds, outbound);
543 }
544
Jian Li614cb092018-07-03 22:41:42 +0900545 private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup,
Jian Li311a9c92018-07-09 16:48:36 +0900546 OpenstackVtapCriterion vTapCriterion, int rulePriority,
Jian Li614cb092018-07-03 22:41:42 +0900547 boolean install) {
548 log.trace("Table Transition: table[{}] -> table[{}] or group[{}]", fromTable, toTable, toGroup);
549
550 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Jian Li311a9c92018-07-09 16:48:36 +0900551 .matchEthType(TYPE_IPV4);
Jian Li614cb092018-07-03 22:41:42 +0900552
Jian Li311a9c92018-07-09 16:48:36 +0900553 // if the IpPrefix is "0.0.0.0/0", we do not include such a match into the flow rule
554 if (!vTapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
555 selectorBuilder.matchIPSrc(vTapCriterion.srcIpPrefix());
556 }
557
558 if (!vTapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
559 selectorBuilder.matchIPDst(vTapCriterion.dstIpPrefix());
560 }
561
562 switch (vTapCriterion.ipProtocol()) {
Jian Li614cb092018-07-03 22:41:42 +0900563 case PROTOCOL_TCP:
Jian Li311a9c92018-07-09 16:48:36 +0900564 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900565
566 // Add port match only if the port number is greater than zero
Jian Li311a9c92018-07-09 16:48:36 +0900567 if (vTapCriterion.srcTpPort().toInt() > 0) {
568 selectorBuilder.matchTcpSrc(vTapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900569 }
Jian Li311a9c92018-07-09 16:48:36 +0900570 if (vTapCriterion.dstTpPort().toInt() > 0) {
571 selectorBuilder.matchTcpDst(vTapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900572 }
573 break;
574 case PROTOCOL_UDP:
Jian Li311a9c92018-07-09 16:48:36 +0900575 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900576
577 // Add port match only if the port number is greater than zero
Jian Li311a9c92018-07-09 16:48:36 +0900578 if (vTapCriterion.srcTpPort().toInt() > 0) {
579 selectorBuilder.matchUdpSrc(vTapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900580 }
Jian Li311a9c92018-07-09 16:48:36 +0900581 if (vTapCriterion.dstTpPort().toInt() > 0) {
582 selectorBuilder.matchUdpDst(vTapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900583 }
584 break;
585 case PROTOCOL_ICMP:
Jian Li311a9c92018-07-09 16:48:36 +0900586 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li614cb092018-07-03 22:41:42 +0900587 break;
588 default:
589 break;
590 }
591
592 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
593 if (toTable != NONE_TABLE) {
594 treatmentBuilder.transition(toTable);
595 } else if (toGroup != NONE_TABLE) {
596 treatmentBuilder.group(GroupId.valueOf(toGroup));
597 } else {
598 log.warn("Not specified toTable or toGroup value");
599 return;
600 }
601
602 FlowRule flowRule = DefaultFlowRule.builder()
603 .forDevice(deviceId)
604 .withSelector(selectorBuilder.build())
605 .withTreatment(treatmentBuilder.build())
606 .withPriority(rulePriority)
607 .fromApp(appId)
608 .makePermanent()
609 .forTable(fromTable)
610 .build();
611
612 applyFlowRule(flowRule, install);
613 }
614
615 private void createOutputTable(DeviceId deviceId, int tableId,
616 PortNumber outPort, VlanId vlanId) {
617 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
618 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
619
620 // Set output port & vlan
621 int priority = PRIORITY_VTAP_DROP;
Jian Li26ef1302018-07-04 14:37:06 +0900622 if (vlanId != null && vlanId.toShort() != UNTAGGED) {
Jian Li614cb092018-07-03 22:41:42 +0900623 treatment.pushVlan().setVlanId(vlanId);
624 }
625 if (outPort != null) {
626 treatment.setOutput(outPort);
627 priority = PRIORITY_VTAP_OUTPORT_RULE;
628 }
629
630 FlowRule flowRule = DefaultFlowRule.builder()
631 .forDevice(deviceId)
632 .withSelector(selector.build())
633 .withTreatment(treatment.build())
634 .withPriority(priority)
635 .makePermanent()
636 .forTable(tableId)
637 .fromApp(appId)
638 .build();
639 applyFlowRule(flowRule, true);
640 }
641
Jian Li26ef1302018-07-04 14:37:06 +0900642 private ExtensionTreatment buildNiciraExtension(DeviceId id, int tableId) {
Jian Li614cb092018-07-03 22:41:42 +0900643 Driver driver = driverService.getDriver(id);
Jian Li26ef1302018-07-04 14:37:06 +0900644 DriverHandler driverHandler =
645 new DefaultDriverHandler(new DefaultDriverData(driver, id));
646 ExtensionTreatmentResolver resolver =
647 driverHandler.behaviour(ExtensionTreatmentResolver.class);
Jian Li614cb092018-07-03 22:41:42 +0900648
Jian Li26ef1302018-07-04 14:37:06 +0900649 ExtensionTreatment extensionInstruction =
650 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
Jian Li614cb092018-07-03 22:41:42 +0900651
652 try {
Jian Li311a9c92018-07-09 16:48:36 +0900653 extensionInstruction.setPropertyValue(TABLE_PROPERTY_KEY, ((short) tableId));
Jian Li614cb092018-07-03 22:41:42 +0900654 } catch (Exception e) {
655 log.error("Failed to set extension treatment for resubmit table {}", id);
656 }
657
658 return extensionInstruction;
659 }
660
661 private void createGroupTable(DeviceId deviceId, int groupId,
662 List<Integer> tableIds, List<PortNumber> ports) {
663 List<GroupBucket> buckets = Lists.newArrayList();
664 tableIds.forEach(tableId -> {
665 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
Jian Li26ef1302018-07-04 14:37:06 +0900666 .extension(buildNiciraExtension(deviceId, tableId), deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900667 GroupBucket bucket = DefaultGroupBucket
668 .createAllGroupBucket(treatment.build());
669 buckets.add(bucket);
670 });
671 ports.forEach(port -> {
672 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
673 .setOutput(port);
674 GroupBucket bucket = DefaultGroupBucket
675 .createAllGroupBucket(treatment.build());
676 buckets.add(bucket);
677 });
678
679 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
680 GroupDescription.Type.ALL,
681 new GroupBuckets(buckets),
682 getGroupKey(groupId),
683 groupId,
684 appId);
685 groupService.addGroup(groupDescription);
686 }
687
Jian Li26ef1302018-07-04 14:37:06 +0900688 private void applyFlowRule(FlowRule flowRule, boolean install) {
689 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li614cb092018-07-03 22:41:42 +0900690
Jian Li26ef1302018-07-04 14:37:06 +0900691 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
Jian Li614cb092018-07-03 22:41:42 +0900692
Jian Li26ef1302018-07-04 14:37:06 +0900693 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
694 @Override
695 public void onSuccess(FlowRuleOperations ops) {
Jian Li311a9c92018-07-09 16:48:36 +0900696 log.debug("Installed flow rules for tapping");
Jian Li26ef1302018-07-04 14:37:06 +0900697 }
Jian Li614cb092018-07-03 22:41:42 +0900698
Jian Li26ef1302018-07-04 14:37:06 +0900699 @Override
700 public void onError(FlowRuleOperations ops) {
Jian Li311a9c92018-07-09 16:48:36 +0900701 log.debug("Failed to install flow rules for tapping");
Jian Li26ef1302018-07-04 14:37:06 +0900702 }
703 }));
704 }
705
706 private class InternalDeviceListener implements DeviceListener {
707 @Override
708 public boolean isRelevant(DeviceEvent event) {
709 // do not allow to proceed without Mastership
710 DeviceId deviceId = event.subject().id();
711 return mastershipService.isLocalMaster(deviceId) &&
712 event.subject().type() == Device.Type.SWITCH;
713 }
714
715 @Override
716 public void event(DeviceEvent event) {
717 DeviceEvent.Type type = event.type();
718 DeviceId deviceId = event.subject().id();
719 log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type);
720
721 switch (type) {
722 case DEVICE_ADDED:
723 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
724 break;
725 default:
726 break;
727 }
728 }
729 }
730
731 private class InternalHostListener implements HostListener {
732 @Override
733 public boolean isRelevant(HostEvent event) {
734 // do not allow to proceed without leadership
735 NodeId leader = leadershipService.getLeader(appId.name());
736 return Objects.equals(localNodeId, leader);
737 }
738
739 @Override
740 public void event(HostEvent event) {
741 HostEvent.Type type = event.type();
742 Host host = event.subject();
743 log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
744
745 switch (type) {
746 case HOST_ADDED:
747 eventExecutor.execute(() -> updateHost(host, null));
748 break;
749
750 case HOST_REMOVED:
751 eventExecutor.execute(() -> updateHost(null, host));
752 break;
753
754 case HOST_UPDATED:
755 case HOST_MOVED:
756 eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
757 break;
758 default:
759 break;
760 }
761 }
762 }
763
764 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
765
766 @Override
767 public boolean isRelevant(OpenstackNodeEvent event) {
768 // do not allow to proceed without leadership
769 NodeId leader = leadershipService.getLeader(appId.name());
770 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
771 }
772
773 @Override
774 public void event(OpenstackNodeEvent event) {
775 DeviceId deviceId = event.subject().intgBridge();
776 switch (event.type()) {
777 case OPENSTACK_NODE_CREATED:
778 case OPENSTACK_NODE_UPDATED:
779 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
780 break;
781 case OPENSTACK_NODE_REMOVED:
Jian Lib1ca1a22018-07-06 13:31:39 +0900782 eventExecutor.execute(() -> clearFlowAndGroupByDeviceId(deviceId));
Ray Milkey41aa8152018-07-06 13:14:33 -0700783 break;
Jian Li26ef1302018-07-04 14:37:06 +0900784 default:
785 break;
786 }
787 }
788 }
789
790 // Store delegate to re-post events emitted from the store.
791 private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
792 @Override
793 public void notify(OpenstackVtapEvent event) {
794 OpenstackVtapEvent.Type type = event.type();
795 OpenstackVtap vTap = event.subject();
796 log.trace("vTapStoreDelegate vTap={}, type={}", vTap, type);
797
798 switch (type) {
799 case VTAP_ADDED:
800 eventExecutor.execute(() -> {
801 // Add new devices
802 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
803 vTap.vTapCriterion(), true);
804 });
805 break;
806
807 case VTAP_UPDATED:
808 OpenstackVtap oldOpenstackVtap = event.prevSubject();
809 eventExecutor.execute(() -> {
810 // Remove excluded devices
811 installFilterRule(
812 Sets.difference(oldOpenstackVtap.txDeviceIds(),
813 vTap.txDeviceIds()),
814 Sets.difference(oldOpenstackVtap.rxDeviceIds(),
815 vTap.rxDeviceIds()),
816 oldOpenstackVtap.vTapCriterion(), false);
817
818 // Add new devices
819 installFilterRule(
820 Sets.difference(vTap.txDeviceIds(),
821 oldOpenstackVtap.txDeviceIds()),
822 Sets.difference(vTap.rxDeviceIds(),
823 oldOpenstackVtap.rxDeviceIds()),
824 vTap.vTapCriterion(), true);
825 });
826 break;
827
828 case VTAP_REMOVED:
829 eventExecutor.execute(() -> {
830 // Remove excluded devices
831 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
832 vTap.vTapCriterion(), false);
833 });
834 break;
835 default:
836 break;
837 }
838 post(event);
839 }
Jian Li38e4d942018-07-03 22:19:16 +0900840 }
Jian Li4f368e82018-07-02 14:22:22 +0900841}