blob: dd76ea9b61884d8efbc87595f50a8bc6f257fe02 [file] [log] [blame]
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001/*
2 * Copyright 2015 Open Networking Laboratory
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.cordvtn;
17
Hyunsun Moonc71231d2015-12-16 20:53:23 -080018import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
20import org.onlab.packet.Ethernet;
Hyunsun Moon4a915152016-01-14 16:56:26 -080021import org.onlab.packet.IPv4;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080022import org.onlab.packet.Ip4Address;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080023import org.onlab.packet.Ip4Prefix;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.IpPrefix;
26import org.onlab.packet.MacAddress;
Hyunsun Moon4a915152016-01-14 16:56:26 -080027import org.onlab.packet.TpPort;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080028import org.onlab.util.ItemNotFoundException;
29import org.onosproject.core.ApplicationId;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080030import org.onosproject.core.DefaultGroupId;
31import org.onosproject.core.GroupId;
32import org.onosproject.mastership.MastershipService;
33import org.onosproject.net.Device;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080034import org.onosproject.net.DeviceId;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080035import org.onosproject.net.Host;
36import org.onosproject.net.PortNumber;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080037import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080038import org.onosproject.net.device.DeviceService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080039import org.onosproject.net.driver.DefaultDriverData;
40import org.onosproject.net.driver.DefaultDriverHandler;
41import org.onosproject.net.driver.Driver;
42import org.onosproject.net.driver.DriverHandler;
43import org.onosproject.net.driver.DriverService;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080044import org.onosproject.net.flow.DefaultFlowRule;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080045import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080047import org.onosproject.net.flow.FlowRule;
48import org.onosproject.net.flow.FlowRuleOperations;
49import org.onosproject.net.flow.FlowRuleOperationsContext;
50import org.onosproject.net.flow.FlowRuleService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080051import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080053import org.onosproject.net.flow.criteria.Criterion;
54import org.onosproject.net.flow.criteria.EthCriterion;
55import org.onosproject.net.flow.criteria.IPCriterion;
56import org.onosproject.net.flow.criteria.PortCriterion;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080057import org.onosproject.net.flow.instructions.ExtensionPropertyException;
58import org.onosproject.net.flow.instructions.ExtensionTreatment;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080059import org.onosproject.net.flow.instructions.Instruction;
60import org.onosproject.net.flow.instructions.Instructions;
61import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
62import org.onosproject.net.group.DefaultGroupBucket;
63import org.onosproject.net.group.DefaultGroupDescription;
64import org.onosproject.net.group.DefaultGroupKey;
65import org.onosproject.net.group.Group;
66import org.onosproject.net.group.GroupBucket;
67import org.onosproject.net.group.GroupBuckets;
68import org.onosproject.net.group.GroupDescription;
69import org.onosproject.net.group.GroupKey;
70import org.onosproject.net.group.GroupService;
71import org.onosproject.openstackswitching.OpenstackNetwork;
72import org.onosproject.openstackswitching.OpenstackSubnet;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080073import org.slf4j.Logger;
74
Hyunsun Moon4161e6f2016-01-07 01:32:31 -080075import java.util.ArrayList;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080076import java.util.List;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080077import java.util.Map;
78import java.util.NoSuchElementException;
79import java.util.Objects;
80import java.util.Set;
81import java.util.stream.Collectors;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080082
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080083import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080084import static org.onosproject.net.Device.Type.SWITCH;
85import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
86import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
87import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080088import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Hyunsun Moonc71231d2015-12-16 20:53:23 -080089import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080090import static org.slf4j.LoggerFactory.getLogger;
91
92/**
Hyunsun Moonc71231d2015-12-16 20:53:23 -080093 * Populates rules for CORD VTN service.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080094 */
Hyunsun Moonc71231d2015-12-16 20:53:23 -080095public class CordVtnRuleInstaller {
96
Hyunsun Moon9f0814b2015-11-04 17:34:35 -080097 protected final Logger log = getLogger(getClass());
98
Hyunsun Moon4a915152016-01-14 16:56:26 -080099 private static final int TABLE_FIRST = 0;
100 private static final int TABLE_IN_PORT = 1;
101 private static final int TABLE_ACCESS_TYPE = 2;
102 private static final int TABLE_IN_SERVICE = 3;
103 private static final int TABLE_DST_IP = 4;
104 private static final int TABLE_TUNNEL_IN = 5;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800105
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800106 private static final int DEFAULT_PRIORITY = 5000;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800107 private static final int LOWER_PRIORITY = 4000;
108 private static final int LOWEST_PRIORITY = 0;
Hyunsun Moon4a915152016-01-14 16:56:26 -0800109 private static final int HIGHER_PRIORITY = 50000;
110
111 private static final int VXLAN_UDP_PORT = 4789;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800112
113 private final ApplicationId appId;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800114 private final FlowRuleService flowRuleService;
115 private final DeviceService deviceService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800116 private final DriverService driverService;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800117 private final GroupService groupService;
118 private final MastershipService mastershipService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800119 private final String tunnelType;
120
121 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800122 * Creates a new rule populator.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800123 *
124 * @param appId application id
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800125 * @param flowRuleService flow rule service
126 * @param deviceService device service
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800127 * @param driverService driver service
128 * @param tunnelType tunnel type
129 */
130 public CordVtnRuleInstaller(ApplicationId appId,
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800131 FlowRuleService flowRuleService,
132 DeviceService deviceService,
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800133 DriverService driverService,
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800134 GroupService groupService,
135 MastershipService mastershipService,
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800136 String tunnelType) {
137 this.appId = appId;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800138 this.flowRuleService = flowRuleService;
139 this.deviceService = deviceService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800140 this.driverService = driverService;
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800141 this.groupService = groupService;
142 this.mastershipService = mastershipService;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800143 this.tunnelType = checkNotNull(tunnelType);
144 }
145
146 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800147 * Installs table miss rule to a give device.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800148 *
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800149 * @param deviceId device id to install the rules
Hyunsun Moon4a915152016-01-14 16:56:26 -0800150 * @param phyPortName physical port name
151 * @param localIp local data plane ip address
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800152 */
Hyunsun Moon4a915152016-01-14 16:56:26 -0800153 public void init(DeviceId deviceId, String phyPortName, IpAddress localIp) {
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800154 // default is drop packets which can be accomplished without
155 // a table miss entry for all table.
Hyunsun Moon4a915152016-01-14 16:56:26 -0800156 PortNumber tunnelPort = getTunnelPort(deviceId);
157 PortNumber phyPort = getPhyPort(deviceId, phyPortName);
158
159 processFirstTable(deviceId, phyPort, localIp);
160 processInPortTable(deviceId, tunnelPort, phyPort);
161 processAccessTypeTable(deviceId, phyPort);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800162 }
163
164 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800165 * Populates basic rules that connect a VM to the other VMs in the system.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800166 *
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800167 * @param host host
168 * @param hostIp host ip
169 * @param tunnelIp tunnel ip
170 * @param vNet openstack network
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800171 */
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800172 public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
173 OpenstackNetwork vNet) {
174 // TODO we can get host ip from host.ip() after applying NetworkConfig host provider
175 checkNotNull(host);
176 checkNotNull(vNet);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800177
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800178 DeviceId deviceId = host.location().deviceId();
179 if (!mastershipService.isLocalMaster(deviceId)) {
180 return;
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800181 }
182
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800183 PortNumber inPort = host.location().port();
184 MacAddress dstMac = host.mac();
185 long tunnelId = Long.parseLong(vNet.segmentId());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800186
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800187 OpenstackSubnet subnet = vNet.subnets().stream()
188 .findFirst()
189 .orElse(null);
190
191 if (subnet == null) {
192 log.error("Failed to get subnet for {}", host.id());
193 return;
194 }
195
196 populateLocalInPortRule(deviceId, inPort, hostIp);
197 populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
198 populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
199 populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800200 }
201
202 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800203 * Removes basic rules related to a given flow information.
204 *
205 * @param host host to be removed
206 */
207 public void removeBasicConnectionRules(Host host) {
208 checkNotNull(host);
209
210 DeviceId deviceId = host.location().deviceId();
211 MacAddress mac = host.mac();
212 PortNumber port = host.location().port();
213 IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
214
215 if (!mastershipService.isLocalMaster(deviceId)) {
216 return;
217 }
218
219 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
220 if (flowRule.deviceId().equals(deviceId)) {
221 PortNumber inPort = getInPort(flowRule);
222 if (inPort != null && inPort.equals(port)) {
223 processFlowRule(false, flowRule);
224 continue;
225 }
226 }
227
228 MacAddress dstMac = getDstMacFromTreatment(flowRule);
229 if (dstMac != null && dstMac.equals(mac)) {
230 processFlowRule(false, flowRule);
231 continue;
232 }
233
234 dstMac = getDstMacFromSelector(flowRule);
235 if (dstMac != null && dstMac.equals(mac)) {
236 processFlowRule(false, flowRule);
237 continue;
238 }
239
240 IpPrefix dstIp = getDstIpFromSelector(flowRule);
241 if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
242 processFlowRule(false, flowRule);
243 }
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800244 }
245
246 // TODO uninstall same network access rule in access table if no vm exists in the network
247 }
248
249 /**
Hyunsun Moon4161e6f2016-01-07 01:32:31 -0800250 * Populates service dependency rules.
251 *
252 * @param tService tenant cord service
253 * @param pService provider cord service
254 */
255 public void populateServiceDependencyRules(CordService tService, CordService pService) {
256 checkNotNull(tService);
257 checkNotNull(pService);
258
259 Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
260 Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
261 Ip4Address serviceIp = pService.serviceIp().getIp4Address();
262
263 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
264 Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
265
266 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
267 GroupId groupId = createServiceGroup(device.id(), pService);
268 outGroups.put(device.id(), groupId);
269
270 Set<PortNumber> vms = tService.hosts().keySet()
271 .stream()
272 .filter(host -> host.location().deviceId().equals(device.id()))
273 .map(host -> host.location().port())
274 .collect(Collectors.toSet());
275 inPorts.put(device.id(), vms);
276 }
277
278 populateIndirectAccessRule(srcRange, serviceIp, outGroups);
279 populateDirectAccessRule(srcRange, dstRange);
280 populateInServiceRule(inPorts, outGroups);
281 }
282
283 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800284 * Removes service dependency rules.
285 *
286 * @param tService tenant cord service
287 * @param pService provider cord service
288 */
289 public void removeServiceDependencyRules(CordService tService, CordService pService) {
290 checkNotNull(tService);
291 checkNotNull(pService);
292
293 Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
294 Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
295 IpPrefix serviceIp = pService.serviceIp().toIpPrefix();
296
297 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
298 GroupKey groupKey = new DefaultGroupKey(pService.id().id().getBytes());
299
300 deviceService.getAvailableDevices(SWITCH).forEach(device -> {
301 Group group = groupService.getGroup(device.id(), groupKey);
302 if (group != null) {
303 outGroups.put(device.id(), group.id());
304 }
305 });
306
307 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
308 IpPrefix dstIp = getDstIpFromSelector(flowRule);
309 IpPrefix srcIp = getSrcIpFromSelector(flowRule);
310
311 if (dstIp != null && dstIp.equals(serviceIp)) {
312 processFlowRule(false, flowRule);
313 continue;
314 }
315
316 if (dstIp != null && srcIp != null) {
317 if (dstIp.equals(dstRange) && srcIp.equals(srcRange)) {
318 processFlowRule(false, flowRule);
319 continue;
320 }
321
322 if (dstIp.equals(srcRange) && srcIp.equals(dstRange)) {
323 processFlowRule(false, flowRule);
324 continue;
325 }
326 }
327
328 GroupId groupId = getGroupIdFromTreatment(flowRule);
329 if (groupId != null && groupId.equals(outGroups.get(flowRule.deviceId()))) {
330 processFlowRule(false, flowRule);
331 }
332 }
333
334 // TODO remove the group if it is not in use
335 }
336
337 /**
Hyunsun Moon4161e6f2016-01-07 01:32:31 -0800338 * Updates group buckets for a given service to all devices.
339 *
340 * @param service cord service
341 */
342 public void updateServiceGroup(CordService service) {
343 checkNotNull(service);
344
345 GroupKey groupKey = getGroupKey(service.id());
346
347 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
348 DeviceId deviceId = device.id();
349 if (!mastershipService.isLocalMaster(deviceId)) {
350 continue;
351 }
352
353 Group group = groupService.getGroup(deviceId, groupKey);
354 if (group == null) {
355 log.debug("No group exists for service {} in {}", service.id(), deviceId);
356 continue;
357 }
358
359 List<GroupBucket> oldBuckets = group.buckets().buckets();
360 List<GroupBucket> newBuckets = getServiceGroupBuckets(
361 deviceId, service.segmentationId(), service.hosts()).buckets();
362
363 if (oldBuckets.equals(newBuckets)) {
364 continue;
365 }
366
367 List<GroupBucket> bucketsToRemove = new ArrayList<>(oldBuckets);
368 bucketsToRemove.removeAll(newBuckets);
369 if (!bucketsToRemove.isEmpty()) {
370 groupService.removeBucketsFromGroup(
371 deviceId,
372 groupKey,
373 new GroupBuckets(bucketsToRemove),
374 groupKey, appId);
375 }
376
377 List<GroupBucket> bucketsToAdd = new ArrayList<>(newBuckets);
378 bucketsToAdd.removeAll(oldBuckets);
379 if (!bucketsToAdd.isEmpty()) {
380 groupService.addBucketsToGroup(
381 deviceId,
382 groupKey,
383 new GroupBuckets(bucketsToAdd),
384 groupKey, appId);
385 }
386 }
387 }
388
389 /**
Hyunsun Moon4a915152016-01-14 16:56:26 -0800390 * Populates default rules on the first table.
391 * The rules are for shuttling vxlan-encapped packets and supporting physical
392 * network connectivity.
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800393 *
394 * @param deviceId device id
Hyunsun Moon4a915152016-01-14 16:56:26 -0800395 * @param phyPort physical port number
396 * @param localIp local data plane ip address
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800397 */
Hyunsun Moon4a915152016-01-14 16:56:26 -0800398 private void processFirstTable(DeviceId deviceId, PortNumber phyPort, IpAddress localIp) {
399 // take vxlan packet out onto the physical port
400 TrafficSelector selector = DefaultTrafficSelector.builder()
401 .matchInPort(PortNumber.LOCAL)
402 .build();
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800403
Hyunsun Moon4a915152016-01-14 16:56:26 -0800404 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
405 .setOutput(phyPort)
406 .build();
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800407
Hyunsun Moon4a915152016-01-14 16:56:26 -0800408 FlowRule flowRule = DefaultFlowRule.builder()
409 .fromApp(appId)
410 .withSelector(selector)
411 .withTreatment(treatment)
412 .withPriority(HIGHER_PRIORITY)
413 .forDevice(deviceId)
414 .forTable(TABLE_FIRST)
415 .makePermanent()
416 .build();
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800417
Hyunsun Moon4a915152016-01-14 16:56:26 -0800418 processFlowRule(true, flowRule);
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800419
Hyunsun Moon4a915152016-01-14 16:56:26 -0800420 // take a vxlan encap'd packet through the Linux stack
421 selector = DefaultTrafficSelector.builder()
422 .matchInPort(phyPort)
423 .matchEthType(Ethernet.TYPE_IPV4)
424 .matchIPProtocol(IPv4.PROTOCOL_UDP)
425 .matchUdpDst(TpPort.tpPort(VXLAN_UDP_PORT))
426 .build();
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800427
Hyunsun Moon4a915152016-01-14 16:56:26 -0800428 treatment = DefaultTrafficTreatment.builder()
429 .setOutput(PortNumber.LOCAL)
430 .build();
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800431
Hyunsun Moon4a915152016-01-14 16:56:26 -0800432 flowRule = DefaultFlowRule.builder()
433 .fromApp(appId)
434 .withSelector(selector)
435 .withTreatment(treatment)
436 .withPriority(HIGHER_PRIORITY)
437 .forDevice(deviceId)
438 .forTable(TABLE_FIRST)
439 .makePermanent()
440 .build();
441
442 processFlowRule(true, flowRule);
443
444 // take all else to the next table
445 selector = DefaultTrafficSelector.builder()
446 .build();
447
448 treatment = DefaultTrafficTreatment.builder()
449 .transition(TABLE_IN_PORT)
450 .build();
451
452 flowRule = DefaultFlowRule.builder()
453 .fromApp(appId)
454 .withSelector(selector)
455 .withTreatment(treatment)
456 .withPriority(LOWEST_PRIORITY)
457 .forDevice(deviceId)
458 .forTable(TABLE_FIRST)
459 .makePermanent()
460 .build();
461
462 processFlowRule(true, flowRule);
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800463 }
464
465 /**
Hyunsun Moon4a915152016-01-14 16:56:26 -0800466 * Forward table miss packets in ACCESS_TYPE table to physical port.
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800467 *
468 * @param deviceId device id
Hyunsun Moon4a915152016-01-14 16:56:26 -0800469 * @param phyPort physical port number
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800470 */
Hyunsun Moon4a915152016-01-14 16:56:26 -0800471 private void processAccessTypeTable(DeviceId deviceId, PortNumber phyPort) {
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800472 TrafficSelector selector = DefaultTrafficSelector.builder()
473 .build();
474
475 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
Hyunsun Moon4a915152016-01-14 16:56:26 -0800476 .setOutput(phyPort)
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800477 .build();
478
479 FlowRule flowRule = DefaultFlowRule.builder()
480 .fromApp(appId)
481 .withSelector(selector)
482 .withTreatment(treatment)
483 .withPriority(LOWEST_PRIORITY)
484 .forDevice(deviceId)
485 .forTable(TABLE_ACCESS_TYPE)
486 .makePermanent()
487 .build();
488
489 processFlowRule(true, flowRule);
490 }
491
492 /**
Hyunsun Moon4a915152016-01-14 16:56:26 -0800493 * Populates default rules for IN_PORT table.
494 * All packets from tunnel port are forwarded to TUNNEL_ID table and all packets
495 * from physical port to ACCESS_TYPE table.
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800496 *
497 * @param deviceId device id to install the rules
Hyunsun Moon4a915152016-01-14 16:56:26 -0800498 * @param tunnelPort tunnel port number
499 * @param phyPort physical port number
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800500 */
Hyunsun Moon4a915152016-01-14 16:56:26 -0800501 private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber phyPort) {
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800502 checkNotNull(tunnelPort);
503
504 TrafficSelector selector = DefaultTrafficSelector.builder()
505 .matchInPort(tunnelPort)
506 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800507
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800508 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
509 .transition(TABLE_TUNNEL_IN)
510 .build();
511
512 FlowRule flowRule = DefaultFlowRule.builder()
513 .fromApp(appId)
514 .withSelector(selector)
515 .withTreatment(treatment)
516 .withPriority(DEFAULT_PRIORITY)
517 .forDevice(deviceId)
518 .forTable(TABLE_IN_PORT)
519 .makePermanent()
520 .build();
521
522 processFlowRule(true, flowRule);
Hyunsun Moon4a915152016-01-14 16:56:26 -0800523
524 selector = DefaultTrafficSelector.builder()
525 .matchInPort(phyPort)
526 .build();
527
528 treatment = DefaultTrafficTreatment.builder()
529 .transition(TABLE_DST_IP)
530 .build();
531
532 flowRule = DefaultFlowRule.builder()
533 .fromApp(appId)
534 .withSelector(selector)
535 .withTreatment(treatment)
536 .withPriority(DEFAULT_PRIORITY)
537 .forDevice(deviceId)
538 .forTable(TABLE_IN_PORT)
539 .makePermanent()
540 .build();
541
542 processFlowRule(true, flowRule);
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800543 }
544
545 /**
546 * Populates rules for local in port in IN_PORT table.
547 * Flows from a given in port, whose source IP is service IP transition
548 * to DST_TYPE table. Other flows transition to IN_SERVICE table.
549 *
550 * @param deviceId device id to install the rules
551 * @param inPort in port
552 * @param srcIp source ip
553 */
554 private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
555 TrafficSelector selector = DefaultTrafficSelector.builder()
556 .matchInPort(inPort)
557 .matchEthType(Ethernet.TYPE_IPV4)
558 .matchIPSrc(srcIp.toIpPrefix())
559 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800560
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800561 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
562 .transition(TABLE_ACCESS_TYPE)
563 .build();
564
565
566 FlowRule flowRule = DefaultFlowRule.builder()
567 .fromApp(appId)
568 .withSelector(selector)
569 .withTreatment(treatment)
570 .withPriority(DEFAULT_PRIORITY)
571 .forDevice(deviceId)
572 .forTable(TABLE_IN_PORT)
573 .makePermanent()
574 .build();
575
576 processFlowRule(true, flowRule);
577
578 selector = DefaultTrafficSelector.builder()
579 .matchInPort(inPort)
580 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800581
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800582 treatment = DefaultTrafficTreatment.builder()
583 .transition(TABLE_IN_SERVICE)
584 .build();
585
586 flowRule = DefaultFlowRule.builder()
587 .fromApp(appId)
588 .withSelector(selector)
589 .withTreatment(treatment)
590 .withPriority(LOWER_PRIORITY)
591 .forDevice(deviceId)
592 .forTable(TABLE_IN_PORT)
593 .makePermanent()
594 .build();
595
596 processFlowRule(true, flowRule);
597 }
598
599 /**
600 * Populates direct VM access rules for ACCESS_TYPE table.
601 * These rules are installed to all devices.
602 *
603 * @param srcRange source ip range
604 * @param dstRange destination ip range
605 */
606 private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
607 TrafficSelector selector = DefaultTrafficSelector.builder()
608 .matchEthType(Ethernet.TYPE_IPV4)
609 .matchIPSrc(srcRange)
610 .matchIPDst(dstRange)
611 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800612
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800613 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
614 .transition(TABLE_DST_IP)
615 .build();
616
617 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
618 FlowRule flowRuleDirect = DefaultFlowRule.builder()
619 .fromApp(appId)
620 .withSelector(selector)
621 .withTreatment(treatment)
622 .withPriority(LOWER_PRIORITY)
623 .forDevice(device.id())
624 .forTable(TABLE_ACCESS_TYPE)
625 .makePermanent()
626 .build();
627
628 processFlowRule(true, flowRuleDirect);
629 }
630 }
631
632 /**
633 * Populates indirect service access rules for ACCESS_TYPE table.
634 * These rules are installed to all devices.
635 *
636 * @param srcRange source range
637 * @param serviceIp service ip
638 * @param outGroups list of output group
639 */
640 private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
641 Map<DeviceId, GroupId> outGroups) {
642 TrafficSelector selector = DefaultTrafficSelector.builder()
643 .matchEthType(Ethernet.TYPE_IPV4)
644 .matchIPSrc(srcRange)
645 .matchIPDst(serviceIp.toIpPrefix())
646 .build();
647
648 for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
649 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
650 .group(outGroup.getValue())
651 .build();
652
653 FlowRule flowRule = DefaultFlowRule.builder()
654 .fromApp(appId)
655 .withSelector(selector)
656 .withTreatment(treatment)
657 .withPriority(DEFAULT_PRIORITY)
658 .forDevice(outGroup.getKey())
659 .forTable(TABLE_ACCESS_TYPE)
660 .makePermanent()
661 .build();
662
663 processFlowRule(true, flowRule);
664 }
665 }
666
667 /**
668 * Populates flow rules for IN_SERVICE table.
669 *
670 * @param inPorts list of inports related to the service for each device
671 * @param outGroups set of output groups
672 */
673 private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
674 checkNotNull(inPorts);
675 checkNotNull(outGroups);
676
677 for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
678 Set<PortNumber> ports = entry.getValue();
679 DeviceId deviceId = entry.getKey();
680
681 GroupId groupId = outGroups.get(deviceId);
682 if (groupId == null) {
683 continue;
684 }
685
686 ports.stream().forEach(port -> {
687 TrafficSelector selector = DefaultTrafficSelector.builder()
688 .matchInPort(port)
689 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800690
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800691 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
692 .group(groupId)
693 .build();
694
695 FlowRule flowRule = DefaultFlowRule.builder()
696 .fromApp(appId)
697 .withSelector(selector)
698 .withTreatment(treatment)
699 .withPriority(DEFAULT_PRIORITY)
700 .forDevice(deviceId)
701 .forTable(TABLE_IN_SERVICE)
702 .makePermanent()
703 .build();
704
705 processFlowRule(true, flowRule);
706 });
707 }
708 }
709
710 /**
711 * Populates flow rules for DST_IP table.
712 *
713 * @param deviceId device id
714 * @param inPort in port
715 * @param dstMac mac address
716 * @param dstIp destination ip
717 * @param tunnelId tunnel id
718 * @param tunnelIp tunnel remote ip
719 */
720 private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
721 IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
722 TrafficSelector selector = DefaultTrafficSelector.builder()
723 .matchEthType(Ethernet.TYPE_IPV4)
724 .matchIPDst(dstIp.toIpPrefix())
725 .build();
Hyunsun Moon4a915152016-01-14 16:56:26 -0800726
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800727 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
728 .setEthDst(dstMac)
729 .setOutput(inPort)
730 .build();
731
732 FlowRule flowRule = DefaultFlowRule.builder()
733 .fromApp(appId)
734 .withSelector(selector)
735 .withTreatment(treatment)
736 .withPriority(DEFAULT_PRIORITY)
737 .forDevice(deviceId)
738 .forTable(TABLE_DST_IP)
739 .makePermanent()
740 .build();
741
742 processFlowRule(true, flowRule);
743
744 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
745 if (device.id().equals(deviceId)) {
746 continue;
747 }
748
749 ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
750 if (tunnelDst == null) {
751 continue;
752 }
753
754 treatment = DefaultTrafficTreatment.builder()
755 .setEthDst(dstMac)
756 .setTunnelId(tunnelId)
757 .extension(tunnelDst, device.id())
758 .setOutput(getTunnelPort(device.id()))
759 .build();
760
761 flowRule = DefaultFlowRule.builder()
762 .fromApp(appId)
763 .withSelector(selector)
764 .withTreatment(treatment)
765 .withPriority(DEFAULT_PRIORITY)
766 .forDevice(device.id())
767 .forTable(TABLE_DST_IP)
768 .makePermanent()
769 .build();
770
771 processFlowRule(true, flowRule);
772 }
773 }
774
775 /**
776 * Populates flow rules for TUNNEL_ID table.
777 *
778 * @param deviceId device id
779 * @param inPort in port
780 * @param mac mac address
781 * @param tunnelId tunnel id
782 */
783 private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
784 TrafficSelector selector = DefaultTrafficSelector.builder()
785 .matchTunnelId(tunnelId)
786 .matchEthDst(mac)
787 .build();
788
789 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
790 .setOutput(inPort)
791 .build();
792
793 FlowRule flowRule = DefaultFlowRule.builder()
794 .fromApp(appId)
795 .withSelector(selector)
796 .withTreatment(treatment)
797 .withPriority(DEFAULT_PRIORITY)
798 .forDevice(deviceId)
799 .forTable(TABLE_TUNNEL_IN)
800 .makePermanent()
801 .build();
802
803 processFlowRule(true, flowRule);
804 }
805
806 /**
807 * Installs or uninstall a given rule.
808 *
809 * @param install true to install, false to uninstall
810 * @param rule rule
811 */
812 private void processFlowRule(boolean install, FlowRule rule) {
813 FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
814 oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
815
816 flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
817 @Override
818 public void onError(FlowRuleOperations ops) {
819 log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
820 }
821 }));
822 }
823
824 /**
825 * Returns tunnel port of the device.
826 *
827 * @param deviceId device id
828 * @return tunnel port number, or null if no tunnel port exists on a given device
829 */
830 private PortNumber getTunnelPort(DeviceId deviceId) {
831 try {
832 return deviceService.getPorts(deviceId).stream()
833 .filter(p -> p.annotations().value("portName").contains(tunnelType))
834 .findFirst().get().number();
835 } catch (NoSuchElementException e) {
836 return null;
837 }
838 }
839
840 /**
Hyunsun Moon4a915152016-01-14 16:56:26 -0800841 * Returns physical port name of a given device.
842 *
843 * @param deviceId device id
844 * @param phyPortName physical port name
845 * @return physical port number, or null if no physical port exists
846 */
847 private PortNumber getPhyPort(DeviceId deviceId, String phyPortName) {
848 try {
849 return deviceService.getPorts(deviceId).stream()
850 .filter(p -> p.annotations().value("portName").contains(phyPortName) &&
851 p.isEnabled())
852 .findFirst().get().number();
853 } catch (NoSuchElementException e) {
854 return null;
855 }
856 }
857
858 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800859 * Returns the inport from a given flow rule if the rule contains the match of it.
860 *
861 * @param flowRule flow rule
862 * @return port number, or null if the rule doesn't have inport match
863 */
864 private PortNumber getInPort(FlowRule flowRule) {
865 Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
866 if (criterion != null && criterion instanceof PortCriterion) {
867 PortCriterion port = (PortCriterion) criterion;
868 return port.port();
869 } else {
870 return null;
871 }
872 }
873
874 /**
875 * Returns the destination mac address from a given flow rule if the rule
876 * contains the instruction of it.
877 *
878 * @param flowRule flow rule
879 * @return mac address, or null if the rule doesn't have destination mac instruction
880 */
881 private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
882 Instruction instruction = flowRule.treatment().allInstructions().stream()
883 .filter(inst -> inst instanceof ModEtherInstruction &&
884 ((ModEtherInstruction) inst).subtype().equals(ETH_DST))
885 .findFirst()
886 .orElse(null);
887
888 if (instruction == null) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800889 return null;
890 }
891
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800892 return ((ModEtherInstruction) instruction).mac();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800893 }
894
895 /**
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800896 * Returns the destination mac address from a given flow rule if the rule
897 * contains the match of it.
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800898 *
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800899 * @param flowRule flow rule
900 * @return mac address, or null if the rule doesn't have destination mac match
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800901 */
Hyunsun Moonc71231d2015-12-16 20:53:23 -0800902 private MacAddress getDstMacFromSelector(FlowRule flowRule) {
903 Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
904 if (criterion != null && criterion instanceof EthCriterion) {
905 EthCriterion eth = (EthCriterion) criterion;
906 return eth.mac();
907 } else {
908 return null;
909 }
910 }
911
912 /**
913 * Returns the destination IP from a given flow rule if the rule contains
914 * the match of it.
915 *
916 * @param flowRule flow rule
917 * @return ip prefix, or null if the rule doesn't have ip match
918 */
919 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
920 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
921 if (criterion != null && criterion instanceof IPCriterion) {
922 IPCriterion ip = (IPCriterion) criterion;
923 return ip.ip();
924 } else {
925 return null;
926 }
927 }
928
929 /**
930 * Returns the source IP from a given flow rule if the rule contains
931 * the match of it.
932 *
933 * @param flowRule flow rule
934 * @return ip prefix, or null if the rule doesn't have ip match
935 */
936 private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
937 Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
938 if (criterion != null && criterion instanceof IPCriterion) {
939 IPCriterion ip = (IPCriterion) criterion;
940 return ip.ip();
941 } else {
942 return null;
943 }
944 }
945
946 /**
947 * Returns the group ID from a given flow rule if the rule contains the
948 * treatment of it.
949 *
950 * @param flowRule flow rule
951 * @return group id, or null if the rule doesn't have group instruction
952 */
953 private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
954 Instruction instruction = flowRule.treatment().allInstructions().stream()
955 .filter(inst -> inst instanceof Instructions.GroupInstruction)
956 .findFirst()
957 .orElse(null);
958
959 if (instruction == null) {
960 return null;
961 }
962
963 return ((Instructions.GroupInstruction) instruction).groupId();
Hyunsun Moon9f0814b2015-11-04 17:34:35 -0800964 }
965
966 /**
Hyunsun Moon4a915152016-01-14 16:56:26 -0800967 * Creates a new group for a given service.
968 *
969 * @param deviceId device id to create a group
970 * @param service cord service
971 * @return group id, or null if it fails to create
972 */
973 private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
974 checkNotNull(service);
975
976 GroupKey groupKey = getGroupKey(service.id());
977 Group group = groupService.getGroup(deviceId, groupKey);
978 GroupId groupId = getGroupId(service.id(), deviceId);
979
980 if (group != null) {
981 log.debug("Group {} is already exist in {}", service.id(), deviceId);
982 return groupId;
983 }
984
985 GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
986 GroupDescription groupDescription = new DefaultGroupDescription(
987 deviceId,
988 GroupDescription.Type.SELECT,
989 buckets,
990 groupKey,
991 groupId.id(),
992 appId);
993
994 groupService.addGroup(groupDescription);
995
996 return groupId;
997 }
998
999 /**
1000 * Returns group buckets for a given device.
1001 *
1002 * @param deviceId device id
1003 * @param tunnelId tunnel id
1004 * @param hosts list of host
1005 * @return group buckets
1006 */
1007 private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
1008 List<GroupBucket> buckets = Lists.newArrayList();
1009
1010 for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
1011 Host host = entry.getKey();
1012 Ip4Address remoteIp = entry.getValue().getIp4Address();
1013 DeviceId hostDevice = host.location().deviceId();
1014
1015 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
1016 .builder()
1017 .setEthDst(host.mac());
1018
1019 if (deviceId.equals(hostDevice)) {
1020 tBuilder.setOutput(host.location().port());
1021 } else {
1022 ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
1023 if (tunnelDst == null) {
1024 continue;
1025 }
1026
1027 tBuilder.extension(tunnelDst, deviceId)
1028 .setTunnelId(tunnelId)
1029 .setOutput(getTunnelPort(hostDevice));
1030 }
1031
1032 buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
1033 }
1034
1035 return new GroupBuckets(buckets);
1036 }
1037
1038 /**
1039 * Returns globally unique group ID.
1040 *
1041 * @param serviceId service id
1042 * @param deviceId device id
1043 * @return group id
1044 */
1045 private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
1046 return new DefaultGroupId(Objects.hash(serviceId, deviceId));
1047 }
1048
1049 /**
1050 * Returns group key of a service.
1051 *
1052 * @param serviceId service id
1053 * @return group key
1054 */
1055 private GroupKey getGroupKey(CordServiceId serviceId) {
1056 return new DefaultGroupKey(serviceId.id().getBytes());
1057 }
1058
1059 /**
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001060 * Returns extension instruction to set tunnel destination.
1061 *
1062 * @param deviceId device id
1063 * @param remoteIp tunnel destination address
1064 * @return extension treatment or null if it fails to get instruction
1065 */
Hyunsun Moonc71231d2015-12-16 20:53:23 -08001066 private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001067 try {
1068 Driver driver = driverService.getDriver(deviceId);
Hyunsun Moonc71231d2015-12-16 20:53:23 -08001069 DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
1070 DriverHandler handler = new DefaultDriverHandler(driverData);
1071 ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001072
Hyunsun Moonc71231d2015-12-16 20:53:23 -08001073 ExtensionTreatment treatment =
1074 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001075 treatment.setPropertyValue("tunnelDst", remoteIp);
1076
1077 return treatment;
Hyunsun Moonc71231d2015-12-16 20:53:23 -08001078 } catch (ItemNotFoundException | UnsupportedOperationException |
1079 ExtensionPropertyException e) {
1080 log.error("Failed to get extension instruction {}", deviceId);
Hyunsun Moon9f0814b2015-11-04 17:34:35 -08001081 return null;
1082 }
1083 }
1084}
Hyunsun Moonc71231d2015-12-16 20:53:23 -08001085