blob: 843a602aec279696ba63a62e07eaf7e4ada6032c [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 Li614cb092018-07-03 22:41:42 +0900180 private final DeviceListener deviceListener = new InternalDeviceListener();
181 private final HostListener hostListener = new InternalHostListener();
Jian Li26ef1302018-07-04 14:37:06 +0900182 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
Jian Li614cb092018-07-03 22:41:42 +0900183
184 private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
185
186 private ApplicationId appId;
187 private NodeId localNodeId;
188 private ScheduledExecutorService eventExecutor;
189
190
191 @Activate
192 public void activate(ComponentContext context) {
193 appId = coreService.registerApplication(APP_ID);
194 localNodeId = clusterService.getLocalNode().id();
195 leadershipService.runForLeadership(appId.name());
196
197 eventExecutor = newSingleThreadScheduledExecutor(
198 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
199
200 store.setDelegate(delegate);
201 eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
202
203 deviceService.addListener(deviceListener);
204 hostService.addListener(hostListener);
Jian Li26ef1302018-07-04 14:37:06 +0900205 osNodeService.addListener(osNodeListener);
206
207 // TODO: need to sweep through device store and add flow rules and
208 // group tables to mirror VM traffic
Jian Li614cb092018-07-03 22:41:42 +0900209
210 log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900211 }
212
Jian Li614cb092018-07-03 22:41:42 +0900213 @Deactivate
214 public void deactivate() {
Jian Li26ef1302018-07-04 14:37:06 +0900215 osNodeService.removeListener(osNodeListener);
Jian Li614cb092018-07-03 22:41:42 +0900216 hostService.removeListener(hostListener);
217 deviceService.removeListener(deviceListener);
Jian Li19f25262018-07-03 22:37:12 +0900218
Jian Li614cb092018-07-03 22:41:42 +0900219 eventDispatcher.removeSink(OpenstackVtapEvent.class);
220 store.unsetDelegate(delegate);
Jian Li19f25262018-07-03 22:37:12 +0900221
Jian Li614cb092018-07-03 22:41:42 +0900222 eventExecutor.shutdown();
223 leadershipService.withdraw(appId.name());
Jian Li19f25262018-07-03 22:37:12 +0900224
Jian Li26ef1302018-07-04 14:37:06 +0900225 // TODO: need to purge vtap related flow rules and group tables
226
Jian Li614cb092018-07-03 22:41:42 +0900227 log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900228 }
229
230 @Override
231 public int getVtapCount(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900232 return store.getVtapCount(type);
Jian Li38e4d942018-07-03 22:19:16 +0900233 }
234
235 @Override
Jian Li19f25262018-07-03 22:37:12 +0900236 public Set<OpenstackVtap> getVtaps(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900237 return store.getVtaps(type);
Jian Li38e4d942018-07-03 22:19:16 +0900238 }
239
240 @Override
241 public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
Jian Li614cb092018-07-03 22:41:42 +0900242 checkNotNull(vTapId, VTAP_ID_NULL);
243 return store.getVtap(vTapId);
Jian Li38e4d942018-07-03 22:19:16 +0900244 }
245
246 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900247 public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
248 DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900249 checkNotNull(deviceId, DEVICE_ID_NULL);
250 return store.getVtapsByDeviceId(type, deviceId);
251 }
252
Jian Li614cb092018-07-03 22:41:42 +0900253 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900254 public OpenstackVtap createVtap(Type type,
255 OpenstackVtapCriterion vTapCriterionOpenstack) {
Jian Li614cb092018-07-03 22:41:42 +0900256 checkNotNull(vTapCriterionOpenstack, VTAP_DESC_NULL);
257
258 Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
259 getEdgeDevice(type, vTapCriterionOpenstack) : ImmutableSet.of();
260 Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
261 getEdgeDevice(type, vTapCriterionOpenstack) : ImmutableSet.of();
262
Jian Li26ef1302018-07-04 14:37:06 +0900263 OpenstackVtap description =
264 DefaultOpenstackVtap.builder()
265 .id(OpenstackVtapId.vTapId())
266 .type(type)
267 .vTapCriterion(vTapCriterionOpenstack)
268 .txDeviceIds(txDevices)
269 .rxDeviceIds(rxDevices)
270 .build();
Jian Li614cb092018-07-03 22:41:42 +0900271 return store.createOrUpdateVtap(description.id(), description, true);
272 }
273
274 @Override
275 public OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap) {
276 checkNotNull(vTapId, VTAP_ID_NULL);
277 checkNotNull(vTap, VTAP_DESC_NULL);
278
279 if (store.getVtap(vTapId) == null) {
280 return null;
281 }
282
283 Set<DeviceId> txDevices = vTap.type().isValid(Type.VTAP_TX) ?
284 getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
285 Set<DeviceId> rxDevices = vTap.type().isValid(Type.VTAP_RX) ?
286 getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
287
Jian Li26ef1302018-07-04 14:37:06 +0900288 DefaultOpenstackVtap description =
289 DefaultOpenstackVtap.builder()
290 .id(vTapId)
291 .type(vTap.type())
292 .vTapCriterion(vTap.vTapCriterion())
293 .txDeviceIds(txDevices)
294 .rxDeviceIds(rxDevices)
295 .build();
Jian Li614cb092018-07-03 22:41:42 +0900296 return store.createOrUpdateVtap(vTapId, description, true);
297 }
298
299 @Override
300 public OpenstackVtap removeVtap(OpenstackVtapId vTapId) {
301 checkNotNull(vTapId, VTAP_ID_NULL);
302 return store.removeVtapById(vTapId);
303 }
304
305 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900306 public void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type,
307 PortNumber portNumber, VlanId vlanId) {
308
Jian Li614cb092018-07-03 22:41:42 +0900309 // Make output table
310 if (type.isValid(Type.VTAP_TX)) {
311 createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, portNumber, vlanId);
312 }
Jian Li26ef1302018-07-04 14:37:06 +0900313
Jian Li614cb092018-07-03 22:41:42 +0900314 if (type.isValid(Type.VTAP_RX)) {
315 createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
316 createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
317 }
318 }
319
320 @Override
321 public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, int vni) {
322 // TODO: need to provide implementation
323 }
324
Jian Li26ef1302018-07-04 14:37:06 +0900325 /**
326 * Obtains the identifier set of edge device where the targeted host is located.
327 * Note that, in most of cases target host is attached to one device,
328 * however, in some cases, the host can be attached to multiple devices.
329 *
330 * @param type vTap type
331 * @param criterion vTap criterion
332 * @return a collection of device identifiers
333 */
334 private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion criterion) {
335 Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
336 StreamSupport.stream(hostService.getHosts().spliterator(), true)
337 .forEach(host -> {
338 if (host.ipAddresses().stream()
339 .anyMatch(ip -> containsIp(type, criterion, ip))) {
340 deviceIds.addAll(host.locations().stream()
341 .map(HostLocation::deviceId)
342 .collect(Collectors.toSet()));
343 }
344 });
345 return deviceIds;
Jian Li614cb092018-07-03 22:41:42 +0900346 }
347
Jian Li26ef1302018-07-04 14:37:06 +0900348 /**
349 * Checks whether the given IP address is included in vTap criterion.
350 * We both check the TX and RX directions.
351 *
352 * @param type vTap type
353 * @param criterion vTap criterion
354 * @param ip IP address
355 * @return boolean value indicates the check result
356 */
357 private boolean containsIp(Type type, OpenstackVtapCriterion criterion, IpAddress ip) {
358 boolean isTxEdge = type.isValid(Type.VTAP_TX) &&
359 criterion.srcIpPrefix().contains(ip);
360 boolean isRxEdge = type.isValid(Type.VTAP_RX) &&
361 criterion.dstIpPrefix().contains(ip);
362
363 return isTxEdge || isRxEdge;
Jian Li614cb092018-07-03 22:41:42 +0900364 }
365
Jian Li26ef1302018-07-04 14:37:06 +0900366 /**
367 * Updates device list of vTaps with respect to the host changes.
368 *
369 * @param newHost new host instance
370 * @param oldHost old host instance
371 */
Jian Li614cb092018-07-03 22:41:42 +0900372 private void updateHost(Host newHost, Host oldHost) {
373 // update devices for vTap tx
374 getVtaps(Type.VTAP_TX).parallelStream().forEach(vTap -> {
Jian Li26ef1302018-07-04 14:37:06 +0900375
376 if (hostDiff(oldHost, newHost, vTap.vTapCriterion().srcIpPrefix())) {
377 oldHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900378 .forEach(deviceId ->
Jian Li26ef1302018-07-04 14:37:06 +0900379 store.removeDeviceFromVtap(vTap.id(), Type.VTAP_TX,
380 oldHost.location().deviceId()));
Jian Li614cb092018-07-03 22:41:42 +0900381 }
Jian Li26ef1302018-07-04 14:37:06 +0900382
383 if (hostDiff(newHost, oldHost, vTap.vTapCriterion().srcIpPrefix())) {
384 newHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900385 .forEach(deviceId ->
386 store.addDeviceToVtap(vTap.id(), Type.VTAP_TX,
387 newHost.location().deviceId()));
388 }
389 });
390
391 // update devices for vTap rx
392 getVtaps(Type.VTAP_RX).parallelStream().forEach(vTap -> {
Jian Li26ef1302018-07-04 14:37:06 +0900393
394 if (hostDiff(oldHost, newHost, vTap.vTapCriterion().dstIpPrefix())) {
395 oldHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900396 .forEach(deviceId ->
397 store.removeDeviceFromVtap(vTap.id(), Type.VTAP_RX,
398 oldHost.location().deviceId()));
399 }
Jian Li26ef1302018-07-04 14:37:06 +0900400
401 if (hostDiff(newHost, oldHost, vTap.vTapCriterion().dstIpPrefix())) {
402 newHost.locations().stream().map(HostLocation::deviceId)
Jian Li614cb092018-07-03 22:41:42 +0900403 .forEach(deviceId ->
404 store.addDeviceToVtap(vTap.id(), Type.VTAP_RX,
405 newHost.location().deviceId()));
406 }
407 });
408 }
409
Jian Li26ef1302018-07-04 14:37:06 +0900410 /**
411 * Checks whether the given IP prefix is contained in the first host rather
412 * than in the second host.
413 *
414 * @param host1 first host instance
415 * @param host2 second host instance
416 * @param ipPrefix IP prefix to be looked up
417 * @return boolean value
418 */
419 private boolean hostDiff(Host host1, Host host2, IpPrefix ipPrefix) {
420 return ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) &&
421 (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains)));
Jian Li614cb092018-07-03 22:41:42 +0900422 }
423
Jian Li26ef1302018-07-04 14:37:06 +0900424 /**
425 * Initializes the flow rules and group table of the given device identifier.
426 *
427 * @param deviceId device identifier
428 */
429 private void initFlowAndGroupByDeviceId(DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900430 // Make vTap pipeline
431 // TODO: need to selective creation by store device consistentMap
Jian Li26ef1302018-07-04 14:37:06 +0900432 initVtapPipeline(deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900433
434 // Install tx filter
435 getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> {
436 connectTables(deviceId,
437 VTAP_INBOUND_TABLE, NONE_TABLE, VTAP_INBOUND_GROUP_TABLE,
438 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
439 });
440
441 // Install rx filter
442 getVtapsByDeviceId(Type.VTAP_RX, deviceId).forEach(vTap -> {
443 connectTables(deviceId,
444 VTAP_FLAT_OUTBOUND_TABLE, NONE_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
445 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
446 connectTables(deviceId,
447 VTAP_OUTBOUND_TABLE, NONE_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
448 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
449 });
450 }
451
Jian Li26ef1302018-07-04 14:37:06 +0900452 /**
453 * Initializes vTap pipeline of the given device.
454 *
455 * @param deviceId device identifier
456 */
457 private void initVtapPipeline(DeviceId deviceId) {
458 // Make output table
459 createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, null, null);
460 createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, null, null);
461 createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, null, null);
462
463 // Make tx group table
464 createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE,
465 ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE),
466 ImmutableList.of());
467
468 // Make rx group table
469 createGroupTable(deviceId, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
470 ImmutableList.of(FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE),
471 ImmutableList.of());
472 createGroupTable(deviceId, VTAP_OUTBOUND_GROUP_TABLE,
473 ImmutableList.of(OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE),
474 ImmutableList.of());
475 }
476
477 /**
478 * Purges all flow rules and group tables using the given device identifier.
479 *
480 * @param deviceId device identifier
481 */
482 private void clearRulesGroupTable(DeviceId deviceId) {
483 Set<FlowRule> purgedRules = Sets.newConcurrentHashSet();
484 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
485 if (flowRule.deviceId().equals(deviceId)) {
486 purgedRules.add(flowRule);
487 }
488 }
489
490 flowRuleService.removeFlowRules((FlowRule[]) purgedRules.toArray());
491
492 groupService.getGroups(deviceId, appId).forEach(group -> {
493 groupService.removeGroup(deviceId, group.appCookie(), appId);
Jian Li614cb092018-07-03 22:41:42 +0900494 });
Jian Li26ef1302018-07-04 14:37:06 +0900495 log.info("OpenstackVtap flow rules and groups are purged");
Jian Li614cb092018-07-03 22:41:42 +0900496 }
497
498 private void installFilterRule(Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
Jian Li26ef1302018-07-04 14:37:06 +0900499 OpenstackVtapCriterion vTapCriterion, boolean install) {
Jian Li614cb092018-07-03 22:41:42 +0900500 final int inbound = 0;
501 final int flatOutbound = 1;
502 final int outbound = 2;
503
504 BiFunction<Set<DeviceId>, Integer, Void> installFlow = (deviceIds, table) -> {
505 int inTable = (table == inbound ? VTAP_INBOUND_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900506 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_TABLE :
507 VTAP_OUTBOUND_TABLE));
508
Jian Li614cb092018-07-03 22:41:42 +0900509 int outGroup = (table == inbound ? VTAP_INBOUND_GROUP_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900510 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_GROUP_TABLE :
511 VTAP_OUTBOUND_GROUP_TABLE));
512
Jian Li614cb092018-07-03 22:41:42 +0900513 deviceIds.stream()
514 .filter(deviceId -> mastershipService.isLocalMaster(deviceId))
515 .forEach(deviceId -> {
Jian Li26ef1302018-07-04 14:37:06 +0900516 connectTables(deviceId, inTable, NONE_TABLE, outGroup,
517 vTapCriterion, PRIORITY_VTAP_RULE, install);
Jian Li614cb092018-07-03 22:41:42 +0900518 });
519 return null;
520 };
521
522 installFlow.apply(txDeviceIds, inbound);
523 installFlow.apply(rxDeviceIds, flatOutbound);
524 installFlow.apply(rxDeviceIds, outbound);
525 }
526
Jian Li614cb092018-07-03 22:41:42 +0900527 private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup,
528 OpenstackVtapCriterion vTapCriterionOpenstack, int rulePriority,
529 boolean install) {
530 log.trace("Table Transition: table[{}] -> table[{}] or group[{}]", fromTable, toTable, toGroup);
531
532 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
533 .matchEthType(TYPE_IPV4)
534 .matchIPSrc(vTapCriterionOpenstack.srcIpPrefix())
535 .matchIPDst(vTapCriterionOpenstack.dstIpPrefix());
536
537 switch (vTapCriterionOpenstack.ipProtocol()) {
538 case PROTOCOL_TCP:
539 selectorBuilder.matchIPProtocol(vTapCriterionOpenstack.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900540
541 // Add port match only if the port number is greater than zero
Jian Li614cb092018-07-03 22:41:42 +0900542 if (vTapCriterionOpenstack.srcTpPort().toInt() > 0) {
543 selectorBuilder.matchTcpSrc(vTapCriterionOpenstack.srcTpPort());
544 }
545 if (vTapCriterionOpenstack.dstTpPort().toInt() > 0) {
546 selectorBuilder.matchTcpDst(vTapCriterionOpenstack.dstTpPort());
547 }
548 break;
549 case PROTOCOL_UDP:
550 selectorBuilder.matchIPProtocol(vTapCriterionOpenstack.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900551
552 // Add port match only if the port number is greater than zero
Jian Li614cb092018-07-03 22:41:42 +0900553 if (vTapCriterionOpenstack.srcTpPort().toInt() > 0) {
554 selectorBuilder.matchUdpSrc(vTapCriterionOpenstack.srcTpPort());
555 }
556 if (vTapCriterionOpenstack.dstTpPort().toInt() > 0) {
557 selectorBuilder.matchUdpDst(vTapCriterionOpenstack.dstTpPort());
558 }
559 break;
560 case PROTOCOL_ICMP:
561 selectorBuilder.matchIPProtocol(vTapCriterionOpenstack.ipProtocol());
562 break;
563 default:
564 break;
565 }
566
567 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
568 if (toTable != NONE_TABLE) {
569 treatmentBuilder.transition(toTable);
570 } else if (toGroup != NONE_TABLE) {
571 treatmentBuilder.group(GroupId.valueOf(toGroup));
572 } else {
573 log.warn("Not specified toTable or toGroup value");
574 return;
575 }
576
577 FlowRule flowRule = DefaultFlowRule.builder()
578 .forDevice(deviceId)
579 .withSelector(selectorBuilder.build())
580 .withTreatment(treatmentBuilder.build())
581 .withPriority(rulePriority)
582 .fromApp(appId)
583 .makePermanent()
584 .forTable(fromTable)
585 .build();
586
587 applyFlowRule(flowRule, install);
588 }
589
590 private void createOutputTable(DeviceId deviceId, int tableId,
591 PortNumber outPort, VlanId vlanId) {
592 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
593 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
594
595 // Set output port & vlan
596 int priority = PRIORITY_VTAP_DROP;
Jian Li26ef1302018-07-04 14:37:06 +0900597 if (vlanId != null && vlanId.toShort() != UNTAGGED) {
Jian Li614cb092018-07-03 22:41:42 +0900598 treatment.pushVlan().setVlanId(vlanId);
599 }
600 if (outPort != null) {
601 treatment.setOutput(outPort);
602 priority = PRIORITY_VTAP_OUTPORT_RULE;
603 }
604
605 FlowRule flowRule = DefaultFlowRule.builder()
606 .forDevice(deviceId)
607 .withSelector(selector.build())
608 .withTreatment(treatment.build())
609 .withPriority(priority)
610 .makePermanent()
611 .forTable(tableId)
612 .fromApp(appId)
613 .build();
614 applyFlowRule(flowRule, true);
615 }
616
Jian Li26ef1302018-07-04 14:37:06 +0900617 private ExtensionTreatment buildNiciraExtension(DeviceId id, int tableId) {
Jian Li614cb092018-07-03 22:41:42 +0900618 Driver driver = driverService.getDriver(id);
Jian Li26ef1302018-07-04 14:37:06 +0900619 DriverHandler driverHandler =
620 new DefaultDriverHandler(new DefaultDriverData(driver, id));
621 ExtensionTreatmentResolver resolver =
622 driverHandler.behaviour(ExtensionTreatmentResolver.class);
Jian Li614cb092018-07-03 22:41:42 +0900623
Jian Li26ef1302018-07-04 14:37:06 +0900624 ExtensionTreatment extensionInstruction =
625 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
Jian Li614cb092018-07-03 22:41:42 +0900626
627 try {
628 extensionInstruction.setPropertyValue("table", ((short) tableId));
629 } catch (Exception e) {
630 log.error("Failed to set extension treatment for resubmit table {}", id);
631 }
632
633 return extensionInstruction;
634 }
635
636 private void createGroupTable(DeviceId deviceId, int groupId,
637 List<Integer> tableIds, List<PortNumber> ports) {
638 List<GroupBucket> buckets = Lists.newArrayList();
639 tableIds.forEach(tableId -> {
640 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
Jian Li26ef1302018-07-04 14:37:06 +0900641 .extension(buildNiciraExtension(deviceId, tableId), deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900642 GroupBucket bucket = DefaultGroupBucket
643 .createAllGroupBucket(treatment.build());
644 buckets.add(bucket);
645 });
646 ports.forEach(port -> {
647 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
648 .setOutput(port);
649 GroupBucket bucket = DefaultGroupBucket
650 .createAllGroupBucket(treatment.build());
651 buckets.add(bucket);
652 });
653
654 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
655 GroupDescription.Type.ALL,
656 new GroupBuckets(buckets),
657 getGroupKey(groupId),
658 groupId,
659 appId);
660 groupService.addGroup(groupDescription);
661 }
662
Jian Li26ef1302018-07-04 14:37:06 +0900663 private void applyFlowRule(FlowRule flowRule, boolean install) {
664 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li614cb092018-07-03 22:41:42 +0900665
Jian Li26ef1302018-07-04 14:37:06 +0900666 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
Jian Li614cb092018-07-03 22:41:42 +0900667
Jian Li26ef1302018-07-04 14:37:06 +0900668 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
669 @Override
670 public void onSuccess(FlowRuleOperations ops) {
671 log.trace("Installed flow rules for tapping");
672 }
Jian Li614cb092018-07-03 22:41:42 +0900673
Jian Li26ef1302018-07-04 14:37:06 +0900674 @Override
675 public void onError(FlowRuleOperations ops) {
676 log.error("Failed to install flow rules for tapping");
677 }
678 }));
679 }
680
681 private class InternalDeviceListener implements DeviceListener {
682 @Override
683 public boolean isRelevant(DeviceEvent event) {
684 // do not allow to proceed without Mastership
685 DeviceId deviceId = event.subject().id();
686 return mastershipService.isLocalMaster(deviceId) &&
687 event.subject().type() == Device.Type.SWITCH;
688 }
689
690 @Override
691 public void event(DeviceEvent event) {
692 DeviceEvent.Type type = event.type();
693 DeviceId deviceId = event.subject().id();
694 log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type);
695
696 switch (type) {
697 case DEVICE_ADDED:
698 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
699 break;
700 default:
701 break;
702 }
703 }
704 }
705
706 private class InternalHostListener implements HostListener {
707 @Override
708 public boolean isRelevant(HostEvent event) {
709 // do not allow to proceed without leadership
710 NodeId leader = leadershipService.getLeader(appId.name());
711 return Objects.equals(localNodeId, leader);
712 }
713
714 @Override
715 public void event(HostEvent event) {
716 HostEvent.Type type = event.type();
717 Host host = event.subject();
718 log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
719
720 switch (type) {
721 case HOST_ADDED:
722 eventExecutor.execute(() -> updateHost(host, null));
723 break;
724
725 case HOST_REMOVED:
726 eventExecutor.execute(() -> updateHost(null, host));
727 break;
728
729 case HOST_UPDATED:
730 case HOST_MOVED:
731 eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
732 break;
733 default:
734 break;
735 }
736 }
737 }
738
739 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
740
741 @Override
742 public boolean isRelevant(OpenstackNodeEvent event) {
743 // do not allow to proceed without leadership
744 NodeId leader = leadershipService.getLeader(appId.name());
745 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
746 }
747
748 @Override
749 public void event(OpenstackNodeEvent event) {
750 DeviceId deviceId = event.subject().intgBridge();
751 switch (event.type()) {
752 case OPENSTACK_NODE_CREATED:
753 case OPENSTACK_NODE_UPDATED:
754 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
755 break;
756 case OPENSTACK_NODE_REMOVED:
757 eventExecutor.execute(() -> clearRulesGroupTable(deviceId));
758 default:
759 break;
760 }
761 }
762 }
763
764 // Store delegate to re-post events emitted from the store.
765 private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
766 @Override
767 public void notify(OpenstackVtapEvent event) {
768 OpenstackVtapEvent.Type type = event.type();
769 OpenstackVtap vTap = event.subject();
770 log.trace("vTapStoreDelegate vTap={}, type={}", vTap, type);
771
772 switch (type) {
773 case VTAP_ADDED:
774 eventExecutor.execute(() -> {
775 // Add new devices
776 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
777 vTap.vTapCriterion(), true);
778 });
779 break;
780
781 case VTAP_UPDATED:
782 OpenstackVtap oldOpenstackVtap = event.prevSubject();
783 eventExecutor.execute(() -> {
784 // Remove excluded devices
785 installFilterRule(
786 Sets.difference(oldOpenstackVtap.txDeviceIds(),
787 vTap.txDeviceIds()),
788 Sets.difference(oldOpenstackVtap.rxDeviceIds(),
789 vTap.rxDeviceIds()),
790 oldOpenstackVtap.vTapCriterion(), false);
791
792 // Add new devices
793 installFilterRule(
794 Sets.difference(vTap.txDeviceIds(),
795 oldOpenstackVtap.txDeviceIds()),
796 Sets.difference(vTap.rxDeviceIds(),
797 oldOpenstackVtap.rxDeviceIds()),
798 vTap.vTapCriterion(), true);
799 });
800 break;
801
802 case VTAP_REMOVED:
803 eventExecutor.execute(() -> {
804 // Remove excluded devices
805 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
806 vTap.vTapCriterion(), false);
807 });
808 break;
809 default:
810 break;
811 }
812 post(event);
813 }
Jian Li38e4d942018-07-03 22:19:16 +0900814 }
Jian Li4f368e82018-07-02 14:22:22 +0900815}