blob: 426e4cb5f6be8ed0540e5338e62ae9343c036c2f [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 Li26ef1302018-07-04 14:37:06 +090022import org.onlab.packet.IpAddress;
Jian Li614cb092018-07-03 22:41:42 +090023import org.onlab.packet.IpPrefix;
Jian Li19f25262018-07-03 22:37:12 +090024import org.onlab.packet.VlanId;
Jian Li614cb092018-07-03 22:41:42 +090025import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipService;
27import org.onosproject.cluster.NodeId;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.core.GroupId;
Jian Li19f25262018-07-03 22:37:12 +090031import org.onosproject.event.AbstractListenerManager;
Jian Li614cb092018-07-03 22:41:42 +090032import org.onosproject.mastership.MastershipService;
Jian Li26ef1302018-07-04 14:37:06 +090033import org.onosproject.net.Device;
Jian Li38e4d942018-07-03 22:19:16 +090034import org.onosproject.net.DeviceId;
Jian Li614cb092018-07-03 22:41:42 +090035import org.onosproject.net.Host;
36import org.onosproject.net.HostLocation;
Jian Li19f25262018-07-03 22:37:12 +090037import org.onosproject.net.PortNumber;
Jian Li614cb092018-07-03 22:41:42 +090038import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
39import org.onosproject.net.device.DeviceEvent;
40import org.onosproject.net.device.DeviceListener;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.driver.DefaultDriverData;
43import org.onosproject.net.driver.DefaultDriverHandler;
44import org.onosproject.net.driver.Driver;
45import org.onosproject.net.driver.DriverHandler;
46import org.onosproject.net.driver.DriverService;
47import org.onosproject.net.flow.DefaultFlowRule;
48import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.FlowRule;
51import org.onosproject.net.flow.FlowRuleOperations;
52import org.onosproject.net.flow.FlowRuleOperationsContext;
53import org.onosproject.net.flow.FlowRuleService;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.flow.instructions.ExtensionTreatment;
Jian Li614cb092018-07-03 22:41:42 +090057import org.onosproject.net.group.DefaultGroupBucket;
58import org.onosproject.net.group.DefaultGroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090059import org.onosproject.net.group.GroupBucket;
60import org.onosproject.net.group.GroupBuckets;
61import org.onosproject.net.group.GroupDescription;
Jian Li614cb092018-07-03 22:41:42 +090062import org.onosproject.net.group.GroupService;
63import org.onosproject.net.host.HostEvent;
64import org.onosproject.net.host.HostListener;
65import org.onosproject.net.host.HostService;
Jian Li26ef1302018-07-04 14:37:06 +090066import org.onosproject.openstacknode.api.OpenstackNodeEvent;
67import org.onosproject.openstacknode.api.OpenstackNodeListener;
68import org.onosproject.openstacknode.api.OpenstackNodeService;
Jian Li38e4d942018-07-03 22:19:16 +090069import org.onosproject.openstackvtap.api.OpenstackVtap;
Jian Li26ef1302018-07-04 14:37:06 +090070import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
Jian Li19f25262018-07-03 22:37:12 +090071import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
72import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
73import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
Jian Li26ef1302018-07-04 14:37:06 +090074import org.onosproject.openstackvtap.api.OpenstackVtapId;
Jian Li38e4d942018-07-03 22:19:16 +090075import org.onosproject.openstackvtap.api.OpenstackVtapListener;
Jian Li4f368e82018-07-02 14:22:22 +090076import org.onosproject.openstackvtap.api.OpenstackVtapService;
Jian Li614cb092018-07-03 22:41:42 +090077import org.onosproject.openstackvtap.api.OpenstackVtapStore;
78import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
Jian Li614cb092018-07-03 22:41:42 +090079import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070080import org.osgi.service.component.annotations.Activate;
81import org.osgi.service.component.annotations.Component;
82import org.osgi.service.component.annotations.Deactivate;
83import org.osgi.service.component.annotations.Reference;
84import org.osgi.service.component.annotations.ReferenceCardinality;
Jian Li614cb092018-07-03 22:41:42 +090085import org.slf4j.Logger;
Jian Li4f368e82018-07-02 14:22:22 +090086
Jian Li614cb092018-07-03 22:41:42 +090087import java.util.List;
88import java.util.Objects;
Jian Li38e4d942018-07-03 22:19:16 +090089import java.util.Set;
Jian Li614cb092018-07-03 22:41:42 +090090import java.util.concurrent.ScheduledExecutorService;
91import java.util.function.BiFunction;
92import java.util.stream.Collectors;
93import java.util.stream.StreamSupport;
94
95import static com.google.common.base.Preconditions.checkNotNull;
96import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
97import static org.onlab.packet.Ethernet.TYPE_IPV4;
98import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
99import static org.onlab.packet.IPv4.PROTOCOL_TCP;
100import static org.onlab.packet.IPv4.PROTOCOL_UDP;
Jian Li26ef1302018-07-04 14:37:06 +0900101import static org.onlab.packet.VlanId.UNTAGGED;
Jian Li614cb092018-07-03 22:41:42 +0900102import static org.onlab.util.Tools.groupedThreads;
Jian Li26ef1302018-07-04 14:37:06 +0900103import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
Jian Li614cb092018-07-03 22:41:42 +0900104import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
105import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
106import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
107import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_GROUP_TABLE;
108import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_MIRROR_TABLE;
109import static org.onosproject.openstacknetworking.api.Constants.VTAP_FLAT_OUTBOUND_TABLE;
110import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_GROUP_TABLE;
111import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_MIRROR_TABLE;
112import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TABLE;
113import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE;
114import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE;
115import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
Jian Li26ef1302018-07-04 14:37:06 +0900116import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
117import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey;
Jian Li614cb092018-07-03 22:41:42 +0900118import static org.slf4j.LoggerFactory.getLogger;
Jian Li38e4d942018-07-03 22:19:16 +0900119
Jian Li4f368e82018-07-02 14:22:22 +0900120/**
Jian Li19f25262018-07-03 22:37:12 +0900121 * Provides basic implementation of the user APIs.
Jian Li4f368e82018-07-02 14:22:22 +0900122 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123@Component(immediate = true, service = { OpenstackVtapService.class, OpenstackVtapAdminService.class })
Jian Li19f25262018-07-03 22:37:12 +0900124public class OpenstackVtapManager
125 extends AbstractListenerManager<OpenstackVtapEvent, OpenstackVtapListener>
126 implements OpenstackVtapService, OpenstackVtapAdminService {
Jian Li4f368e82018-07-02 14:22:22 +0900127
Jian Li614cb092018-07-03 22:41:42 +0900128 private final Logger log = getLogger(getClass());
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900131 protected CoreService coreService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900134 protected ClusterService clusterService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900137 protected LeadershipService leadershipService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900140 protected MastershipService mastershipService;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900143 protected DriverService driverService;
144
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700145 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900146 protected FlowRuleService flowRuleService;
147
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900149 protected GroupService groupService;
150
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900152 protected DeviceService deviceService;
153
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900155 protected HostService hostService;
156
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li614cb092018-07-03 22:41:42 +0900158 protected OpenstackVtapStore store;
159
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li26ef1302018-07-04 14:37:06 +0900161 protected OpenstackNodeService osNodeService;
162
Jian Li614cb092018-07-03 22:41:42 +0900163 public static final String APP_ID = "org.onosproject.openstackvtap";
164
165 public static final String VTAP_ID_NULL = "OpenstackVtap ID cannot be null";
166 public static final String VTAP_DESC_NULL = "OpenstackVtap fields cannot be null";
167 public static final String DEVICE_ID_NULL = "Device ID cannot be null";
168
169 private static final int PRIORITY_VTAP_RULE = 50000;
170 private static final int PRIORITY_VTAP_OUTPORT_RULE = 1000;
171 private static final int PRIORITY_VTAP_DROP = 0;
172
173 private static final int NONE_TABLE = -1;
174 private static final int INBOUND_NEXT_TABLE = DHCP_ARP_TABLE;
175 private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
176 private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
177
Jian Li311a9c92018-07-09 16:48:36 +0900178 private static final IpPrefix ARBITRARY_IP_PREFIX =
179 IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0);
180 private static final String TABLE_PROPERTY_KEY = "table";
181
Jian Li614cb092018-07-03 22:41:42 +0900182 private final DeviceListener deviceListener = new InternalDeviceListener();
183 private final HostListener hostListener = new InternalHostListener();
Jian Li26ef1302018-07-04 14:37:06 +0900184 private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
Jian Li614cb092018-07-03 22:41:42 +0900185
186 private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
187
188 private ApplicationId appId;
189 private NodeId localNodeId;
190 private ScheduledExecutorService eventExecutor;
191
192
193 @Activate
194 public void activate(ComponentContext context) {
195 appId = coreService.registerApplication(APP_ID);
196 localNodeId = clusterService.getLocalNode().id();
197 leadershipService.runForLeadership(appId.name());
198
199 eventExecutor = newSingleThreadScheduledExecutor(
200 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
201
202 store.setDelegate(delegate);
203 eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
204
205 deviceService.addListener(deviceListener);
206 hostService.addListener(hostListener);
Jian Li26ef1302018-07-04 14:37:06 +0900207 osNodeService.addListener(osNodeListener);
208
Jian Lib1ca1a22018-07-06 13:31:39 +0900209 initFlowAndGroupForCompNodes();
Jian Li614cb092018-07-03 22:41:42 +0900210
211 log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900212 }
213
Jian Li614cb092018-07-03 22:41:42 +0900214 @Deactivate
215 public void deactivate() {
Jian Lib1ca1a22018-07-06 13:31:39 +0900216 clearFlowAndGroupForCompNodes();
217
Jian Li26ef1302018-07-04 14:37:06 +0900218 osNodeService.removeListener(osNodeListener);
Jian Li614cb092018-07-03 22:41:42 +0900219 hostService.removeListener(hostListener);
220 deviceService.removeListener(deviceListener);
Jian Li19f25262018-07-03 22:37:12 +0900221
Jian Li614cb092018-07-03 22:41:42 +0900222 eventDispatcher.removeSink(OpenstackVtapEvent.class);
223 store.unsetDelegate(delegate);
Jian Li19f25262018-07-03 22:37:12 +0900224
Jian Li614cb092018-07-03 22:41:42 +0900225 eventExecutor.shutdown();
226 leadershipService.withdraw(appId.name());
Jian Li19f25262018-07-03 22:37:12 +0900227
Jian Li614cb092018-07-03 22:41:42 +0900228 log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName());
Jian Li19f25262018-07-03 22:37:12 +0900229 }
230
231 @Override
232 public int getVtapCount(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900233 return store.getVtapCount(type);
Jian Li38e4d942018-07-03 22:19:16 +0900234 }
235
236 @Override
Jian Li19f25262018-07-03 22:37:12 +0900237 public Set<OpenstackVtap> getVtaps(Type type) {
Jian Li614cb092018-07-03 22:41:42 +0900238 return store.getVtaps(type);
Jian Li38e4d942018-07-03 22:19:16 +0900239 }
240
241 @Override
242 public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
Jian Li614cb092018-07-03 22:41:42 +0900243 checkNotNull(vTapId, VTAP_ID_NULL);
244 return store.getVtap(vTapId);
Jian Li38e4d942018-07-03 22:19:16 +0900245 }
246
247 @Override
Jian Li26ef1302018-07-04 14:37:06 +0900248 public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
249 DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900250 checkNotNull(deviceId, DEVICE_ID_NULL);
251 return store.getVtapsByDeviceId(type, deviceId);
252 }
253
Jian Li614cb092018-07-03 22:41:42 +0900254 @Override
Jian Li311a9c92018-07-09 16:48:36 +0900255 public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterion) {
256 checkNotNull(vTapCriterion, VTAP_DESC_NULL);
Jian Li614cb092018-07-03 22:41:42 +0900257
258 Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
Jian Li311a9c92018-07-09 16:48:36 +0900259 getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900260 Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
Jian Li311a9c92018-07-09 16:48:36 +0900261 getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
Jian Li614cb092018-07-03 22:41:42 +0900262
Jian Li26ef1302018-07-04 14:37:06 +0900263 OpenstackVtap description =
264 DefaultOpenstackVtap.builder()
265 .id(OpenstackVtapId.vTapId())
266 .type(type)
Jian Li311a9c92018-07-09 16:48:36 +0900267 .vTapCriterion(vTapCriterion)
Jian Li26ef1302018-07-04 14:37:06 +0900268 .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 /**
Jian Lib1ca1a22018-07-06 13:31:39 +0900425 * Initializes the flow rules and group tables for all completed compute nodes.
426 */
427 private void initFlowAndGroupForCompNodes() {
428 osNodeService.completeNodes(COMPUTE).forEach(node ->
429 initFlowAndGroupByDeviceId(node.intgBridge()));
430 }
431
432 /**
Jian Li26ef1302018-07-04 14:37:06 +0900433 * Initializes the flow rules and group table of the given device identifier.
434 *
435 * @param deviceId device identifier
436 */
437 private void initFlowAndGroupByDeviceId(DeviceId deviceId) {
Jian Li614cb092018-07-03 22:41:42 +0900438 // Make vTap pipeline
439 // TODO: need to selective creation by store device consistentMap
Jian Li26ef1302018-07-04 14:37:06 +0900440 initVtapPipeline(deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900441
442 // Install tx filter
443 getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> {
444 connectTables(deviceId,
445 VTAP_INBOUND_TABLE, NONE_TABLE, VTAP_INBOUND_GROUP_TABLE,
446 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
447 });
448
449 // Install rx filter
450 getVtapsByDeviceId(Type.VTAP_RX, deviceId).forEach(vTap -> {
451 connectTables(deviceId,
452 VTAP_FLAT_OUTBOUND_TABLE, NONE_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
453 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
454 connectTables(deviceId,
455 VTAP_OUTBOUND_TABLE, NONE_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
456 vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
457 });
458 }
459
Jian Li26ef1302018-07-04 14:37:06 +0900460 /**
461 * Initializes vTap pipeline of the given device.
462 *
463 * @param deviceId device identifier
464 */
465 private void initVtapPipeline(DeviceId deviceId) {
466 // Make output table
467 createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, null, null);
468 createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, null, null);
469 createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, null, null);
470
471 // Make tx group table
472 createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE,
473 ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE),
474 ImmutableList.of());
475
476 // Make rx group table
477 createGroupTable(deviceId, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
478 ImmutableList.of(FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE),
479 ImmutableList.of());
480 createGroupTable(deviceId, VTAP_OUTBOUND_GROUP_TABLE,
481 ImmutableList.of(OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE),
482 ImmutableList.of());
483 }
484
485 /**
Jian Lib1ca1a22018-07-06 13:31:39 +0900486 * Purges all flow rules and group tables for completed compute nodes.
487 */
488 private void clearFlowAndGroupForCompNodes() {
489 osNodeService.completeNodes(COMPUTE).forEach(node ->
490 clearFlowAndGroupByDeviceId(node.intgBridge()));
491 }
492
493 /**
Jian Li26ef1302018-07-04 14:37:06 +0900494 * Purges all flow rules and group tables using the given device identifier.
495 *
496 * @param deviceId device identifier
497 */
Jian Lib1ca1a22018-07-06 13:31:39 +0900498 private void clearFlowAndGroupByDeviceId(DeviceId deviceId) {
Jian Li26ef1302018-07-04 14:37:06 +0900499 Set<FlowRule> purgedRules = Sets.newConcurrentHashSet();
500 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
501 if (flowRule.deviceId().equals(deviceId)) {
502 purgedRules.add(flowRule);
503 }
504 }
505
Jian Libd295cd2018-07-22 11:53:57 +0900506 flowRuleService.removeFlowRules(purgedRules.toArray(new FlowRule[0]));
Jian Li26ef1302018-07-04 14:37:06 +0900507
508 groupService.getGroups(deviceId, appId).forEach(group -> {
509 groupService.removeGroup(deviceId, group.appCookie(), appId);
Jian Li614cb092018-07-03 22:41:42 +0900510 });
Jian Li26ef1302018-07-04 14:37:06 +0900511 log.info("OpenstackVtap flow rules and groups are purged");
Jian Li614cb092018-07-03 22:41:42 +0900512 }
513
514 private void installFilterRule(Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
Jian Li26ef1302018-07-04 14:37:06 +0900515 OpenstackVtapCriterion vTapCriterion, boolean install) {
Jian Li614cb092018-07-03 22:41:42 +0900516 final int inbound = 0;
517 final int flatOutbound = 1;
518 final int outbound = 2;
519
520 BiFunction<Set<DeviceId>, Integer, Void> installFlow = (deviceIds, table) -> {
521 int inTable = (table == inbound ? VTAP_INBOUND_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900522 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_TABLE :
523 VTAP_OUTBOUND_TABLE));
524
Jian Li614cb092018-07-03 22:41:42 +0900525 int outGroup = (table == inbound ? VTAP_INBOUND_GROUP_TABLE :
Jian Li26ef1302018-07-04 14:37:06 +0900526 (table == flatOutbound ? VTAP_FLAT_OUTBOUND_GROUP_TABLE :
527 VTAP_OUTBOUND_GROUP_TABLE));
528
Jian Li614cb092018-07-03 22:41:42 +0900529 deviceIds.stream()
530 .filter(deviceId -> mastershipService.isLocalMaster(deviceId))
531 .forEach(deviceId -> {
Jian Li26ef1302018-07-04 14:37:06 +0900532 connectTables(deviceId, inTable, NONE_TABLE, outGroup,
533 vTapCriterion, PRIORITY_VTAP_RULE, install);
Jian Li614cb092018-07-03 22:41:42 +0900534 });
535 return null;
536 };
537
538 installFlow.apply(txDeviceIds, inbound);
539 installFlow.apply(rxDeviceIds, flatOutbound);
540 installFlow.apply(rxDeviceIds, outbound);
541 }
542
Jian Li614cb092018-07-03 22:41:42 +0900543 private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup,
Jian Li311a9c92018-07-09 16:48:36 +0900544 OpenstackVtapCriterion vTapCriterion, int rulePriority,
Jian Li614cb092018-07-03 22:41:42 +0900545 boolean install) {
546 log.trace("Table Transition: table[{}] -> table[{}] or group[{}]", fromTable, toTable, toGroup);
547
548 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Jian Li311a9c92018-07-09 16:48:36 +0900549 .matchEthType(TYPE_IPV4);
Jian Li614cb092018-07-03 22:41:42 +0900550
Jian Li311a9c92018-07-09 16:48:36 +0900551 // if the IpPrefix is "0.0.0.0/0", we do not include such a match into the flow rule
552 if (!vTapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
553 selectorBuilder.matchIPSrc(vTapCriterion.srcIpPrefix());
554 }
555
556 if (!vTapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
557 selectorBuilder.matchIPDst(vTapCriterion.dstIpPrefix());
558 }
559
560 switch (vTapCriterion.ipProtocol()) {
Jian Li614cb092018-07-03 22:41:42 +0900561 case PROTOCOL_TCP:
Jian Li311a9c92018-07-09 16:48:36 +0900562 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900563
564 // Add port match only if the port number is greater than zero
Jian Li311a9c92018-07-09 16:48:36 +0900565 if (vTapCriterion.srcTpPort().toInt() > 0) {
566 selectorBuilder.matchTcpSrc(vTapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900567 }
Jian Li311a9c92018-07-09 16:48:36 +0900568 if (vTapCriterion.dstTpPort().toInt() > 0) {
569 selectorBuilder.matchTcpDst(vTapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900570 }
571 break;
572 case PROTOCOL_UDP:
Jian Li311a9c92018-07-09 16:48:36 +0900573 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li26ef1302018-07-04 14:37:06 +0900574
575 // Add port match only if the port number is greater than zero
Jian Li311a9c92018-07-09 16:48:36 +0900576 if (vTapCriterion.srcTpPort().toInt() > 0) {
577 selectorBuilder.matchUdpSrc(vTapCriterion.srcTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900578 }
Jian Li311a9c92018-07-09 16:48:36 +0900579 if (vTapCriterion.dstTpPort().toInt() > 0) {
580 selectorBuilder.matchUdpDst(vTapCriterion.dstTpPort());
Jian Li614cb092018-07-03 22:41:42 +0900581 }
582 break;
583 case PROTOCOL_ICMP:
Jian Li311a9c92018-07-09 16:48:36 +0900584 selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
Jian Li614cb092018-07-03 22:41:42 +0900585 break;
586 default:
587 break;
588 }
589
590 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
591 if (toTable != NONE_TABLE) {
592 treatmentBuilder.transition(toTable);
593 } else if (toGroup != NONE_TABLE) {
594 treatmentBuilder.group(GroupId.valueOf(toGroup));
595 } else {
596 log.warn("Not specified toTable or toGroup value");
597 return;
598 }
599
600 FlowRule flowRule = DefaultFlowRule.builder()
601 .forDevice(deviceId)
602 .withSelector(selectorBuilder.build())
603 .withTreatment(treatmentBuilder.build())
604 .withPriority(rulePriority)
605 .fromApp(appId)
606 .makePermanent()
607 .forTable(fromTable)
608 .build();
609
610 applyFlowRule(flowRule, install);
611 }
612
613 private void createOutputTable(DeviceId deviceId, int tableId,
614 PortNumber outPort, VlanId vlanId) {
615 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
616 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
617
618 // Set output port & vlan
619 int priority = PRIORITY_VTAP_DROP;
Jian Li26ef1302018-07-04 14:37:06 +0900620 if (vlanId != null && vlanId.toShort() != UNTAGGED) {
Jian Li614cb092018-07-03 22:41:42 +0900621 treatment.pushVlan().setVlanId(vlanId);
622 }
623 if (outPort != null) {
624 treatment.setOutput(outPort);
625 priority = PRIORITY_VTAP_OUTPORT_RULE;
626 }
627
628 FlowRule flowRule = DefaultFlowRule.builder()
629 .forDevice(deviceId)
630 .withSelector(selector.build())
631 .withTreatment(treatment.build())
632 .withPriority(priority)
633 .makePermanent()
634 .forTable(tableId)
635 .fromApp(appId)
636 .build();
637 applyFlowRule(flowRule, true);
638 }
639
Jian Li26ef1302018-07-04 14:37:06 +0900640 private ExtensionTreatment buildNiciraExtension(DeviceId id, int tableId) {
Jian Li614cb092018-07-03 22:41:42 +0900641 Driver driver = driverService.getDriver(id);
Jian Li26ef1302018-07-04 14:37:06 +0900642 DriverHandler driverHandler =
643 new DefaultDriverHandler(new DefaultDriverData(driver, id));
644 ExtensionTreatmentResolver resolver =
645 driverHandler.behaviour(ExtensionTreatmentResolver.class);
Jian Li614cb092018-07-03 22:41:42 +0900646
Jian Li26ef1302018-07-04 14:37:06 +0900647 ExtensionTreatment extensionInstruction =
648 resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
Jian Li614cb092018-07-03 22:41:42 +0900649
650 try {
Jian Li311a9c92018-07-09 16:48:36 +0900651 extensionInstruction.setPropertyValue(TABLE_PROPERTY_KEY, ((short) tableId));
Jian Li614cb092018-07-03 22:41:42 +0900652 } catch (Exception e) {
653 log.error("Failed to set extension treatment for resubmit table {}", id);
654 }
655
656 return extensionInstruction;
657 }
658
659 private void createGroupTable(DeviceId deviceId, int groupId,
660 List<Integer> tableIds, List<PortNumber> ports) {
661 List<GroupBucket> buckets = Lists.newArrayList();
662 tableIds.forEach(tableId -> {
663 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
Jian Li26ef1302018-07-04 14:37:06 +0900664 .extension(buildNiciraExtension(deviceId, tableId), deviceId);
Jian Li614cb092018-07-03 22:41:42 +0900665 GroupBucket bucket = DefaultGroupBucket
666 .createAllGroupBucket(treatment.build());
667 buckets.add(bucket);
668 });
669 ports.forEach(port -> {
670 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
671 .setOutput(port);
672 GroupBucket bucket = DefaultGroupBucket
673 .createAllGroupBucket(treatment.build());
674 buckets.add(bucket);
675 });
676
677 GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
678 GroupDescription.Type.ALL,
679 new GroupBuckets(buckets),
680 getGroupKey(groupId),
681 groupId,
682 appId);
683 groupService.addGroup(groupDescription);
684 }
685
Jian Li26ef1302018-07-04 14:37:06 +0900686 private void applyFlowRule(FlowRule flowRule, boolean install) {
687 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Jian Li614cb092018-07-03 22:41:42 +0900688
Jian Li26ef1302018-07-04 14:37:06 +0900689 flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
Jian Li614cb092018-07-03 22:41:42 +0900690
Jian Li26ef1302018-07-04 14:37:06 +0900691 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
692 @Override
693 public void onSuccess(FlowRuleOperations ops) {
Jian Li311a9c92018-07-09 16:48:36 +0900694 log.debug("Installed flow rules for tapping");
Jian Li26ef1302018-07-04 14:37:06 +0900695 }
Jian Li614cb092018-07-03 22:41:42 +0900696
Jian Li26ef1302018-07-04 14:37:06 +0900697 @Override
698 public void onError(FlowRuleOperations ops) {
Jian Li311a9c92018-07-09 16:48:36 +0900699 log.debug("Failed to install flow rules for tapping");
Jian Li26ef1302018-07-04 14:37:06 +0900700 }
701 }));
702 }
703
704 private class InternalDeviceListener implements DeviceListener {
705 @Override
706 public boolean isRelevant(DeviceEvent event) {
707 // do not allow to proceed without Mastership
708 DeviceId deviceId = event.subject().id();
709 return mastershipService.isLocalMaster(deviceId) &&
710 event.subject().type() == Device.Type.SWITCH;
711 }
712
713 @Override
714 public void event(DeviceEvent event) {
715 DeviceEvent.Type type = event.type();
716 DeviceId deviceId = event.subject().id();
717 log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type);
718
719 switch (type) {
720 case DEVICE_ADDED:
721 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
722 break;
723 default:
724 break;
725 }
726 }
727 }
728
729 private class InternalHostListener implements HostListener {
730 @Override
731 public boolean isRelevant(HostEvent event) {
732 // do not allow to proceed without leadership
733 NodeId leader = leadershipService.getLeader(appId.name());
734 return Objects.equals(localNodeId, leader);
735 }
736
737 @Override
738 public void event(HostEvent event) {
739 HostEvent.Type type = event.type();
740 Host host = event.subject();
741 log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
742
743 switch (type) {
744 case HOST_ADDED:
745 eventExecutor.execute(() -> updateHost(host, null));
746 break;
747
748 case HOST_REMOVED:
749 eventExecutor.execute(() -> updateHost(null, host));
750 break;
751
752 case HOST_UPDATED:
753 case HOST_MOVED:
754 eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
755 break;
756 default:
757 break;
758 }
759 }
760 }
761
762 private class InternalOpenstackNodeListener implements OpenstackNodeListener {
763
764 @Override
765 public boolean isRelevant(OpenstackNodeEvent event) {
766 // do not allow to proceed without leadership
767 NodeId leader = leadershipService.getLeader(appId.name());
768 return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
769 }
770
771 @Override
772 public void event(OpenstackNodeEvent event) {
773 DeviceId deviceId = event.subject().intgBridge();
774 switch (event.type()) {
775 case OPENSTACK_NODE_CREATED:
776 case OPENSTACK_NODE_UPDATED:
777 eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
778 break;
779 case OPENSTACK_NODE_REMOVED:
Jian Lib1ca1a22018-07-06 13:31:39 +0900780 eventExecutor.execute(() -> clearFlowAndGroupByDeviceId(deviceId));
Ray Milkey41aa8152018-07-06 13:14:33 -0700781 break;
Jian Li26ef1302018-07-04 14:37:06 +0900782 default:
783 break;
784 }
785 }
786 }
787
788 // Store delegate to re-post events emitted from the store.
789 private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
790 @Override
791 public void notify(OpenstackVtapEvent event) {
792 OpenstackVtapEvent.Type type = event.type();
793 OpenstackVtap vTap = event.subject();
794 log.trace("vTapStoreDelegate vTap={}, type={}", vTap, type);
795
796 switch (type) {
797 case VTAP_ADDED:
798 eventExecutor.execute(() -> {
799 // Add new devices
800 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
801 vTap.vTapCriterion(), true);
802 });
803 break;
804
805 case VTAP_UPDATED:
806 OpenstackVtap oldOpenstackVtap = event.prevSubject();
807 eventExecutor.execute(() -> {
808 // Remove excluded devices
809 installFilterRule(
810 Sets.difference(oldOpenstackVtap.txDeviceIds(),
811 vTap.txDeviceIds()),
812 Sets.difference(oldOpenstackVtap.rxDeviceIds(),
813 vTap.rxDeviceIds()),
814 oldOpenstackVtap.vTapCriterion(), false);
815
816 // Add new devices
817 installFilterRule(
818 Sets.difference(vTap.txDeviceIds(),
819 oldOpenstackVtap.txDeviceIds()),
820 Sets.difference(vTap.rxDeviceIds(),
821 oldOpenstackVtap.rxDeviceIds()),
822 vTap.vTapCriterion(), true);
823 });
824 break;
825
826 case VTAP_REMOVED:
827 eventExecutor.execute(() -> {
828 // Remove excluded devices
829 installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
830 vTap.vTapCriterion(), false);
831 });
832 break;
833 default:
834 break;
835 }
836 post(event);
837 }
Jian Li38e4d942018-07-03 22:19:16 +0900838 }
Jian Li4f368e82018-07-02 14:22:22 +0900839}