blob: 4e044e111be0503d37cbb03837e7837d54cce77e [file] [log] [blame]
Hyunsun Moon05f528a2015-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 Moonba290072015-12-16 20:53:23 -080018import com.google.common.collect.Lists;
19import com.google.common.collect.Maps;
20import org.onlab.packet.Ethernet;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080021import org.onlab.packet.Ip4Address;
Hyunsun Moonba290072015-12-16 20:53:23 -080022import org.onlab.packet.Ip4Prefix;
23import org.onlab.packet.IpAddress;
24import org.onlab.packet.IpPrefix;
25import org.onlab.packet.MacAddress;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080026import org.onlab.util.ItemNotFoundException;
27import org.onosproject.core.ApplicationId;
Hyunsun Moonba290072015-12-16 20:53:23 -080028import org.onosproject.core.DefaultGroupId;
29import org.onosproject.core.GroupId;
30import org.onosproject.mastership.MastershipService;
31import org.onosproject.net.Device;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080032import org.onosproject.net.DeviceId;
Hyunsun Moonba290072015-12-16 20:53:23 -080033import org.onosproject.net.Host;
34import org.onosproject.net.PortNumber;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080035import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
Hyunsun Moonba290072015-12-16 20:53:23 -080036import org.onosproject.net.device.DeviceService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080037import org.onosproject.net.driver.DefaultDriverData;
38import org.onosproject.net.driver.DefaultDriverHandler;
39import org.onosproject.net.driver.Driver;
40import org.onosproject.net.driver.DriverHandler;
41import org.onosproject.net.driver.DriverService;
Hyunsun Moonba290072015-12-16 20:53:23 -080042import org.onosproject.net.flow.DefaultFlowRule;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080043import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moonba290072015-12-16 20:53:23 -080045import org.onosproject.net.flow.FlowRule;
46import org.onosproject.net.flow.FlowRuleOperations;
47import org.onosproject.net.flow.FlowRuleOperationsContext;
48import org.onosproject.net.flow.FlowRuleService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080049import org.onosproject.net.flow.TrafficSelector;
50import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moonba290072015-12-16 20:53:23 -080051import org.onosproject.net.flow.criteria.Criterion;
52import org.onosproject.net.flow.criteria.EthCriterion;
53import org.onosproject.net.flow.criteria.IPCriterion;
54import org.onosproject.net.flow.criteria.PortCriterion;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080055import org.onosproject.net.flow.instructions.ExtensionPropertyException;
56import org.onosproject.net.flow.instructions.ExtensionTreatment;
Hyunsun Moonba290072015-12-16 20:53:23 -080057import org.onosproject.net.flow.instructions.Instruction;
58import org.onosproject.net.flow.instructions.Instructions;
59import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
60import org.onosproject.net.group.DefaultGroupBucket;
61import org.onosproject.net.group.DefaultGroupDescription;
62import org.onosproject.net.group.DefaultGroupKey;
63import org.onosproject.net.group.Group;
64import org.onosproject.net.group.GroupBucket;
65import org.onosproject.net.group.GroupBuckets;
66import org.onosproject.net.group.GroupDescription;
67import org.onosproject.net.group.GroupKey;
68import org.onosproject.net.group.GroupService;
69import org.onosproject.openstackswitching.OpenstackNetwork;
70import org.onosproject.openstackswitching.OpenstackSubnet;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080071import org.slf4j.Logger;
72
73import java.util.List;
Hyunsun Moonba290072015-12-16 20:53:23 -080074import java.util.Map;
75import java.util.NoSuchElementException;
76import java.util.Objects;
77import java.util.Set;
78import java.util.stream.Collectors;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080079
Hyunsun Moon05f528a2015-11-04 17:34:35 -080080import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moonba290072015-12-16 20:53:23 -080081import static org.onosproject.net.Device.Type.SWITCH;
82import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
83import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
84import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080085import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Hyunsun Moonba290072015-12-16 20:53:23 -080086import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080087import static org.slf4j.LoggerFactory.getLogger;
88
89/**
Hyunsun Moonba290072015-12-16 20:53:23 -080090 * Populates rules for CORD VTN service.
Hyunsun Moon05f528a2015-11-04 17:34:35 -080091 */
Hyunsun Moonba290072015-12-16 20:53:23 -080092public class CordVtnRuleInstaller {
93
Hyunsun Moon05f528a2015-11-04 17:34:35 -080094 protected final Logger log = getLogger(getClass());
95
Hyunsun Moonba290072015-12-16 20:53:23 -080096 private static final int TABLE_IN_PORT = 0;
97 private static final int TABLE_ACCESS_TYPE = 1;
98 private static final int TABLE_IN_SERVICE = 2;
99 private static final int TABLE_DST_IP = 3;
100 private static final int TABLE_TUNNEL_IN = 4;
101
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800102 private static final int DEFAULT_PRIORITY = 5000;
Hyunsun Moonba290072015-12-16 20:53:23 -0800103 private static final int LOWER_PRIORITY = 4000;
104 private static final int LOWEST_PRIORITY = 0;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800105
106 private final ApplicationId appId;
Hyunsun Moonba290072015-12-16 20:53:23 -0800107 private final FlowRuleService flowRuleService;
108 private final DeviceService deviceService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800109 private final DriverService driverService;
Hyunsun Moonba290072015-12-16 20:53:23 -0800110 private final GroupService groupService;
111 private final MastershipService mastershipService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800112 private final String tunnelType;
113
114 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800115 * Creates a new rule populator.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800116 *
117 * @param appId application id
Hyunsun Moonba290072015-12-16 20:53:23 -0800118 * @param flowRuleService flow rule service
119 * @param deviceService device service
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800120 * @param driverService driver service
121 * @param tunnelType tunnel type
122 */
123 public CordVtnRuleInstaller(ApplicationId appId,
Hyunsun Moonba290072015-12-16 20:53:23 -0800124 FlowRuleService flowRuleService,
125 DeviceService deviceService,
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800126 DriverService driverService,
Hyunsun Moonba290072015-12-16 20:53:23 -0800127 GroupService groupService,
128 MastershipService mastershipService,
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800129 String tunnelType) {
130 this.appId = appId;
Hyunsun Moonba290072015-12-16 20:53:23 -0800131 this.flowRuleService = flowRuleService;
132 this.deviceService = deviceService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800133 this.driverService = driverService;
Hyunsun Moonba290072015-12-16 20:53:23 -0800134 this.groupService = groupService;
135 this.mastershipService = mastershipService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800136 this.tunnelType = checkNotNull(tunnelType);
137 }
138
139 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800140 * Installs table miss rule to a give device.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800141 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800142 * @param deviceId device id to install the rules
143 * @param tunnelPort tunnel port number of the device
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800144 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800145 public void init(DeviceId deviceId, PortNumber tunnelPort) {
146 // default is drop packets which can be accomplished without
147 // a table miss entry for all table.
148 populateTunnelInPortRule(deviceId, tunnelPort);
149 processAccessTypeTable(deviceId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800150 }
151
152 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800153 * Populates basic rules that connect a VM to the other VMs in the system.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800154 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800155 * @param host host
156 * @param hostIp host ip
157 * @param tunnelIp tunnel ip
158 * @param vNet openstack network
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800159 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800160 public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
161 OpenstackNetwork vNet) {
162 // TODO we can get host ip from host.ip() after applying NetworkConfig host provider
163 checkNotNull(host);
164 checkNotNull(vNet);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800165
Hyunsun Moonba290072015-12-16 20:53:23 -0800166 DeviceId deviceId = host.location().deviceId();
167 if (!mastershipService.isLocalMaster(deviceId)) {
168 return;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800169 }
170
Hyunsun Moonba290072015-12-16 20:53:23 -0800171 PortNumber inPort = host.location().port();
172 MacAddress dstMac = host.mac();
173 long tunnelId = Long.parseLong(vNet.segmentId());
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800174
Hyunsun Moonba290072015-12-16 20:53:23 -0800175 OpenstackSubnet subnet = vNet.subnets().stream()
176 .findFirst()
177 .orElse(null);
178
179 if (subnet == null) {
180 log.error("Failed to get subnet for {}", host.id());
181 return;
182 }
183
184 populateLocalInPortRule(deviceId, inPort, hostIp);
185 populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
186 populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
187 populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800188 }
189
190 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800191 * Populates service dependency rules.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800192 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800193 * @param tService tenant cord service
194 * @param pService provider cord service
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800195 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800196 public void populateServiceDependencyRules(CordService tService, CordService pService) {
197 checkNotNull(tService);
198 checkNotNull(pService);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800199
Hyunsun Moonba290072015-12-16 20:53:23 -0800200 Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
201 Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
202 Ip4Address serviceIp = pService.serviceIp().getIp4Address();
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800203
Hyunsun Moonba290072015-12-16 20:53:23 -0800204 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
205 Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
206
207 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
208 GroupId groupId = createServiceGroup(device.id(), pService);
209 outGroups.put(device.id(), groupId);
210
211 Set<PortNumber> vms = tService.hosts().keySet()
212 .stream()
213 .filter(host -> host.location().deviceId().equals(device.id()))
214 .map(host -> host.location().port())
215 .collect(Collectors.toSet());
216 inPorts.put(device.id(), vms);
217 }
218
219 populateIndirectAccessRule(srcRange, serviceIp, outGroups);
220 populateDirectAccessRule(srcRange, dstRange);
221 populateInServiceRule(inPorts, outGroups);
222 }
223
224 /**
225 * Removes basic rules related to a given flow information.
226 *
227 * @param host host to be removed
228 */
229 public void removeBasicConnectionRules(Host host) {
230 checkNotNull(host);
231
232 DeviceId deviceId = host.location().deviceId();
233 MacAddress mac = host.mac();
234 PortNumber port = host.location().port();
235 IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
236
237 if (!mastershipService.isLocalMaster(deviceId)) {
238 return;
239 }
240
241 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
242 if (flowRule.deviceId().equals(deviceId)) {
243 PortNumber inPort = getInPort(flowRule);
244 if (inPort != null && inPort.equals(port)) {
245 processFlowRule(false, flowRule);
246 continue;
247 }
248 }
249
250 MacAddress dstMac = getDstMacFromTreatment(flowRule);
251 if (dstMac != null && dstMac.equals(mac)) {
252 processFlowRule(false, flowRule);
253 continue;
254 }
255
256 dstMac = getDstMacFromSelector(flowRule);
257 if (dstMac != null && dstMac.equals(mac)) {
258 processFlowRule(false, flowRule);
259 continue;
260 }
261
262 IpPrefix dstIp = getDstIpFromSelector(flowRule);
263 if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
264 processFlowRule(false, flowRule);
265 }
266
267 }
268
269 // TODO uninstall same network access rule in access table if no vm exists in the network
270 }
271
272 /**
273 * Removes service dependency rules.
274 *
275 * @param tService tenant cord service
276 * @param pService provider cord service
277 */
278 public void removeServiceDependencyRules(CordService tService, CordService pService) {
279 checkNotNull(tService);
280 checkNotNull(pService);
281
282 Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
283 Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
284 IpPrefix serviceIp = pService.serviceIp().toIpPrefix();
285
286 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
287 GroupKey groupKey = new DefaultGroupKey(pService.id().id().getBytes());
288
289 deviceService.getAvailableDevices(SWITCH).forEach(device -> {
290 Group group = groupService.getGroup(device.id(), groupKey);
291 if (group != null) {
292 outGroups.put(device.id(), group.id());
293 }
294 });
295
296 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
297 IpPrefix dstIp = getDstIpFromSelector(flowRule);
298 IpPrefix srcIp = getSrcIpFromSelector(flowRule);
299
300 if (dstIp != null && dstIp.equals(serviceIp)) {
301 processFlowRule(false, flowRule);
302 continue;
303 }
304
305 if (dstIp != null && srcIp != null) {
306 if (dstIp.equals(dstRange) && srcIp.equals(srcRange)) {
307 processFlowRule(false, flowRule);
308 continue;
309 }
310
311 if (dstIp.equals(srcRange) && srcIp.equals(dstRange)) {
312 processFlowRule(false, flowRule);
313 continue;
314 }
315 }
316
317 GroupId groupId = getGroupIdFromTreatment(flowRule);
318 if (groupId != null && groupId.equals(outGroups.get(flowRule.deviceId()))) {
319 processFlowRule(false, flowRule);
320 }
321 }
322
323 // TODO remove the group if it is not in use
324 }
325
326 /**
327 * Creates a new group for a given service.
328 *
329 * @param deviceId device id to create a group
330 * @param service cord service
331 * @return group id, or null if it fails to create
332 */
333 private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
334 checkNotNull(service);
335
336 GroupKey groupKey = getGroupKey(service.id());
337 Group group = groupService.getGroup(deviceId, groupKey);
338 GroupId groupId = getGroupId(service.id(), deviceId);
339
340 if (group != null) {
341 log.debug("Group {} is already exist in {}", service.id(), deviceId);
342 return groupId;
343 }
344
345 GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
346 GroupDescription groupDescription = new DefaultGroupDescription(
347 deviceId,
348 GroupDescription.Type.SELECT,
349 buckets,
350 groupKey,
351 groupId.id(),
352 appId);
353
354 groupService.addGroup(groupDescription);
355
356 return groupId;
357 }
358
359 /**
360 * Returns group buckets for a given device.
361 *
362 * @param deviceId device id
363 * @param tunnelId tunnel id
364 * @param hosts list of host
365 * @return group buckets
366 */
367 private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
368 List<GroupBucket> buckets = Lists.newArrayList();
369
370 for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
371 Host host = entry.getKey();
372 Ip4Address remoteIp = entry.getValue().getIp4Address();
373 DeviceId hostDevice = host.location().deviceId();
374
375 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
376 .builder()
377 .setEthDst(host.mac());
378
379 if (deviceId.equals(hostDevice)) {
380 tBuilder.setOutput(host.location().port());
381 } else {
382 ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
383 if (tunnelDst == null) {
384 continue;
385 }
386
387 tBuilder.extension(tunnelDst, deviceId)
388 .setTunnelId(tunnelId)
389 .setOutput(getTunnelPort(hostDevice));
390 }
391
392 buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
393 }
394
395 return new GroupBuckets(buckets);
396 }
397
398 /**
399 * Returns globally unique group ID.
400 *
401 * @param serviceId service id
402 * @param deviceId device id
403 * @return group id
404 */
405 private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
406 return new DefaultGroupId(Objects.hash(serviceId, deviceId));
407 }
408
409 /**
410 * Returns group key of a service.
411 *
412 * @param serviceId service id
413 * @return group key
414 */
415 private GroupKey getGroupKey(CordServiceId serviceId) {
416 return new DefaultGroupKey(serviceId.id().getBytes());
417 }
418
419 /**
420 * Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
421 *
422 * @param deviceId device id
423 */
424 private void processAccessTypeTable(DeviceId deviceId) {
425 TrafficSelector selector = DefaultTrafficSelector.builder()
426 .build();
427
428 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
429 .transition(TABLE_IN_SERVICE)
430 .build();
431
432 FlowRule flowRule = DefaultFlowRule.builder()
433 .fromApp(appId)
434 .withSelector(selector)
435 .withTreatment(treatment)
436 .withPriority(LOWEST_PRIORITY)
437 .forDevice(deviceId)
438 .forTable(TABLE_ACCESS_TYPE)
439 .makePermanent()
440 .build();
441
442 processFlowRule(true, flowRule);
443 }
444
445 /**
446 * Populates rules for tunnel flows in port in IN_PORT table.
447 * All flows from tunnel port are forwarded to TUNNEL_ID table.
448 *
449 * @param deviceId device id to install the rules
450 * @param tunnelPort tunnel port
451 */
452 private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
453 checkNotNull(tunnelPort);
454
455 TrafficSelector selector = DefaultTrafficSelector.builder()
456 .matchInPort(tunnelPort)
457 .build();
458 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
459 .transition(TABLE_TUNNEL_IN)
460 .build();
461
462 FlowRule flowRule = DefaultFlowRule.builder()
463 .fromApp(appId)
464 .withSelector(selector)
465 .withTreatment(treatment)
466 .withPriority(DEFAULT_PRIORITY)
467 .forDevice(deviceId)
468 .forTable(TABLE_IN_PORT)
469 .makePermanent()
470 .build();
471
472 processFlowRule(true, flowRule);
473 }
474
475 /**
476 * Populates rules for local in port in IN_PORT table.
477 * Flows from a given in port, whose source IP is service IP transition
478 * to DST_TYPE table. Other flows transition to IN_SERVICE table.
479 *
480 * @param deviceId device id to install the rules
481 * @param inPort in port
482 * @param srcIp source ip
483 */
484 private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
485 TrafficSelector selector = DefaultTrafficSelector.builder()
486 .matchInPort(inPort)
487 .matchEthType(Ethernet.TYPE_IPV4)
488 .matchIPSrc(srcIp.toIpPrefix())
489 .build();
490 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
491 .transition(TABLE_ACCESS_TYPE)
492 .build();
493
494
495 FlowRule flowRule = DefaultFlowRule.builder()
496 .fromApp(appId)
497 .withSelector(selector)
498 .withTreatment(treatment)
499 .withPriority(DEFAULT_PRIORITY)
500 .forDevice(deviceId)
501 .forTable(TABLE_IN_PORT)
502 .makePermanent()
503 .build();
504
505 processFlowRule(true, flowRule);
506
507 selector = DefaultTrafficSelector.builder()
508 .matchInPort(inPort)
509 .build();
510 treatment = DefaultTrafficTreatment.builder()
511 .transition(TABLE_IN_SERVICE)
512 .build();
513
514 flowRule = DefaultFlowRule.builder()
515 .fromApp(appId)
516 .withSelector(selector)
517 .withTreatment(treatment)
518 .withPriority(LOWER_PRIORITY)
519 .forDevice(deviceId)
520 .forTable(TABLE_IN_PORT)
521 .makePermanent()
522 .build();
523
524 processFlowRule(true, flowRule);
525 }
526
527 /**
528 * Populates direct VM access rules for ACCESS_TYPE table.
529 * These rules are installed to all devices.
530 *
531 * @param srcRange source ip range
532 * @param dstRange destination ip range
533 */
534 private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
535 TrafficSelector selector = DefaultTrafficSelector.builder()
536 .matchEthType(Ethernet.TYPE_IPV4)
537 .matchIPSrc(srcRange)
538 .matchIPDst(dstRange)
539 .build();
540 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
541 .transition(TABLE_DST_IP)
542 .build();
543
544 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
545 FlowRule flowRuleDirect = DefaultFlowRule.builder()
546 .fromApp(appId)
547 .withSelector(selector)
548 .withTreatment(treatment)
549 .withPriority(LOWER_PRIORITY)
550 .forDevice(device.id())
551 .forTable(TABLE_ACCESS_TYPE)
552 .makePermanent()
553 .build();
554
555 processFlowRule(true, flowRuleDirect);
556 }
557 }
558
559 /**
560 * Populates indirect service access rules for ACCESS_TYPE table.
561 * These rules are installed to all devices.
562 *
563 * @param srcRange source range
564 * @param serviceIp service ip
565 * @param outGroups list of output group
566 */
567 private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
568 Map<DeviceId, GroupId> outGroups) {
569 TrafficSelector selector = DefaultTrafficSelector.builder()
570 .matchEthType(Ethernet.TYPE_IPV4)
571 .matchIPSrc(srcRange)
572 .matchIPDst(serviceIp.toIpPrefix())
573 .build();
574
575 for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
576 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
577 .group(outGroup.getValue())
578 .build();
579
580 FlowRule flowRule = DefaultFlowRule.builder()
581 .fromApp(appId)
582 .withSelector(selector)
583 .withTreatment(treatment)
584 .withPriority(DEFAULT_PRIORITY)
585 .forDevice(outGroup.getKey())
586 .forTable(TABLE_ACCESS_TYPE)
587 .makePermanent()
588 .build();
589
590 processFlowRule(true, flowRule);
591 }
592 }
593
594 /**
595 * Populates flow rules for IN_SERVICE table.
596 *
597 * @param inPorts list of inports related to the service for each device
598 * @param outGroups set of output groups
599 */
600 private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
601 checkNotNull(inPorts);
602 checkNotNull(outGroups);
603
604 for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
605 Set<PortNumber> ports = entry.getValue();
606 DeviceId deviceId = entry.getKey();
607
608 GroupId groupId = outGroups.get(deviceId);
609 if (groupId == null) {
610 continue;
611 }
612
613 ports.stream().forEach(port -> {
614 TrafficSelector selector = DefaultTrafficSelector.builder()
615 .matchInPort(port)
616 .build();
617 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
618 .group(groupId)
619 .build();
620
621 FlowRule flowRule = DefaultFlowRule.builder()
622 .fromApp(appId)
623 .withSelector(selector)
624 .withTreatment(treatment)
625 .withPriority(DEFAULT_PRIORITY)
626 .forDevice(deviceId)
627 .forTable(TABLE_IN_SERVICE)
628 .makePermanent()
629 .build();
630
631 processFlowRule(true, flowRule);
632 });
633 }
634 }
635
636 /**
637 * Populates flow rules for DST_IP table.
638 *
639 * @param deviceId device id
640 * @param inPort in port
641 * @param dstMac mac address
642 * @param dstIp destination ip
643 * @param tunnelId tunnel id
644 * @param tunnelIp tunnel remote ip
645 */
646 private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
647 IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
648 TrafficSelector selector = DefaultTrafficSelector.builder()
649 .matchEthType(Ethernet.TYPE_IPV4)
650 .matchIPDst(dstIp.toIpPrefix())
651 .build();
652 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
653 .setEthDst(dstMac)
654 .setOutput(inPort)
655 .build();
656
657 FlowRule flowRule = DefaultFlowRule.builder()
658 .fromApp(appId)
659 .withSelector(selector)
660 .withTreatment(treatment)
661 .withPriority(DEFAULT_PRIORITY)
662 .forDevice(deviceId)
663 .forTable(TABLE_DST_IP)
664 .makePermanent()
665 .build();
666
667 processFlowRule(true, flowRule);
668
669 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
670 if (device.id().equals(deviceId)) {
671 continue;
672 }
673
674 ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
675 if (tunnelDst == null) {
676 continue;
677 }
678
679 treatment = DefaultTrafficTreatment.builder()
680 .setEthDst(dstMac)
681 .setTunnelId(tunnelId)
682 .extension(tunnelDst, device.id())
683 .setOutput(getTunnelPort(device.id()))
684 .build();
685
686 flowRule = DefaultFlowRule.builder()
687 .fromApp(appId)
688 .withSelector(selector)
689 .withTreatment(treatment)
690 .withPriority(DEFAULT_PRIORITY)
691 .forDevice(device.id())
692 .forTable(TABLE_DST_IP)
693 .makePermanent()
694 .build();
695
696 processFlowRule(true, flowRule);
697 }
698 }
699
700 /**
701 * Populates flow rules for TUNNEL_ID table.
702 *
703 * @param deviceId device id
704 * @param inPort in port
705 * @param mac mac address
706 * @param tunnelId tunnel id
707 */
708 private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
709 TrafficSelector selector = DefaultTrafficSelector.builder()
710 .matchTunnelId(tunnelId)
711 .matchEthDst(mac)
712 .build();
713
714 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
715 .setOutput(inPort)
716 .build();
717
718 FlowRule flowRule = DefaultFlowRule.builder()
719 .fromApp(appId)
720 .withSelector(selector)
721 .withTreatment(treatment)
722 .withPriority(DEFAULT_PRIORITY)
723 .forDevice(deviceId)
724 .forTable(TABLE_TUNNEL_IN)
725 .makePermanent()
726 .build();
727
728 processFlowRule(true, flowRule);
729 }
730
731 /**
732 * Installs or uninstall a given rule.
733 *
734 * @param install true to install, false to uninstall
735 * @param rule rule
736 */
737 private void processFlowRule(boolean install, FlowRule rule) {
738 FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
739 oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
740
741 flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
742 @Override
743 public void onError(FlowRuleOperations ops) {
744 log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
745 }
746 }));
747 }
748
749 /**
750 * Returns tunnel port of the device.
751 *
752 * @param deviceId device id
753 * @return tunnel port number, or null if no tunnel port exists on a given device
754 */
755 private PortNumber getTunnelPort(DeviceId deviceId) {
756 try {
757 return deviceService.getPorts(deviceId).stream()
758 .filter(p -> p.annotations().value("portName").contains(tunnelType))
759 .findFirst().get().number();
760 } catch (NoSuchElementException e) {
761 return null;
762 }
763 }
764
765 /**
766 * Returns the inport from a given flow rule if the rule contains the match of it.
767 *
768 * @param flowRule flow rule
769 * @return port number, or null if the rule doesn't have inport match
770 */
771 private PortNumber getInPort(FlowRule flowRule) {
772 Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
773 if (criterion != null && criterion instanceof PortCriterion) {
774 PortCriterion port = (PortCriterion) criterion;
775 return port.port();
776 } else {
777 return null;
778 }
779 }
780
781 /**
782 * Returns the destination mac address from a given flow rule if the rule
783 * contains the instruction of it.
784 *
785 * @param flowRule flow rule
786 * @return mac address, or null if the rule doesn't have destination mac instruction
787 */
788 private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
789 Instruction instruction = flowRule.treatment().allInstructions().stream()
790 .filter(inst -> inst instanceof ModEtherInstruction &&
791 ((ModEtherInstruction) inst).subtype().equals(ETH_DST))
792 .findFirst()
793 .orElse(null);
794
795 if (instruction == null) {
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800796 return null;
797 }
798
Hyunsun Moonba290072015-12-16 20:53:23 -0800799 return ((ModEtherInstruction) instruction).mac();
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800800 }
801
802 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800803 * Returns the destination mac address from a given flow rule if the rule
804 * contains the match of it.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800805 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800806 * @param flowRule flow rule
807 * @return mac address, or null if the rule doesn't have destination mac match
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800808 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800809 private MacAddress getDstMacFromSelector(FlowRule flowRule) {
810 Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
811 if (criterion != null && criterion instanceof EthCriterion) {
812 EthCriterion eth = (EthCriterion) criterion;
813 return eth.mac();
814 } else {
815 return null;
816 }
817 }
818
819 /**
820 * Returns the destination IP from a given flow rule if the rule contains
821 * the match of it.
822 *
823 * @param flowRule flow rule
824 * @return ip prefix, or null if the rule doesn't have ip match
825 */
826 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
827 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
828 if (criterion != null && criterion instanceof IPCriterion) {
829 IPCriterion ip = (IPCriterion) criterion;
830 return ip.ip();
831 } else {
832 return null;
833 }
834 }
835
836 /**
837 * Returns the source IP from a given flow rule if the rule contains
838 * the match of it.
839 *
840 * @param flowRule flow rule
841 * @return ip prefix, or null if the rule doesn't have ip match
842 */
843 private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
844 Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
845 if (criterion != null && criterion instanceof IPCriterion) {
846 IPCriterion ip = (IPCriterion) criterion;
847 return ip.ip();
848 } else {
849 return null;
850 }
851 }
852
853 /**
854 * Returns the group ID from a given flow rule if the rule contains the
855 * treatment of it.
856 *
857 * @param flowRule flow rule
858 * @return group id, or null if the rule doesn't have group instruction
859 */
860 private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
861 Instruction instruction = flowRule.treatment().allInstructions().stream()
862 .filter(inst -> inst instanceof Instructions.GroupInstruction)
863 .findFirst()
864 .orElse(null);
865
866 if (instruction == null) {
867 return null;
868 }
869
870 return ((Instructions.GroupInstruction) instruction).groupId();
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800871 }
872
873 /**
874 * Returns extension instruction to set tunnel destination.
875 *
876 * @param deviceId device id
877 * @param remoteIp tunnel destination address
878 * @return extension treatment or null if it fails to get instruction
879 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800880 private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800881 try {
882 Driver driver = driverService.getDriver(deviceId);
Hyunsun Moonba290072015-12-16 20:53:23 -0800883 DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
884 DriverHandler handler = new DefaultDriverHandler(driverData);
885 ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800886
Hyunsun Moonba290072015-12-16 20:53:23 -0800887 ExtensionTreatment treatment =
888 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800889 treatment.setPropertyValue("tunnelDst", remoteIp);
890
891 return treatment;
Hyunsun Moonba290072015-12-16 20:53:23 -0800892 } catch (ItemNotFoundException | UnsupportedOperationException |
893 ExtensionPropertyException e) {
894 log.error("Failed to get extension instruction {}", deviceId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800895 return null;
896 }
897 }
898}
Hyunsun Moonba290072015-12-16 20:53:23 -0800899