blob: 1984629745a8568ef63fa3cfa94aae14d1d49ae0 [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
Hyunsun Moonee37c442016-01-07 01:32:31 -080073import java.util.ArrayList;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080074import java.util.List;
Hyunsun Moonba290072015-12-16 20:53:23 -080075import java.util.Map;
76import java.util.NoSuchElementException;
77import java.util.Objects;
78import java.util.Set;
79import java.util.stream.Collectors;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080080
Hyunsun Moon05f528a2015-11-04 17:34:35 -080081import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moonba290072015-12-16 20:53:23 -080082import static org.onosproject.net.Device.Type.SWITCH;
83import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
84import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
85import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080086import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
Hyunsun Moonba290072015-12-16 20:53:23 -080087import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
Hyunsun Moon05f528a2015-11-04 17:34:35 -080088import static org.slf4j.LoggerFactory.getLogger;
89
90/**
Hyunsun Moonba290072015-12-16 20:53:23 -080091 * Populates rules for CORD VTN service.
Hyunsun Moon05f528a2015-11-04 17:34:35 -080092 */
Hyunsun Moonba290072015-12-16 20:53:23 -080093public class CordVtnRuleInstaller {
94
Hyunsun Moon05f528a2015-11-04 17:34:35 -080095 protected final Logger log = getLogger(getClass());
96
Hyunsun Moonba290072015-12-16 20:53:23 -080097 private static final int TABLE_IN_PORT = 0;
98 private static final int TABLE_ACCESS_TYPE = 1;
99 private static final int TABLE_IN_SERVICE = 2;
100 private static final int TABLE_DST_IP = 3;
101 private static final int TABLE_TUNNEL_IN = 4;
102
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800103 private static final int DEFAULT_PRIORITY = 5000;
Hyunsun Moonba290072015-12-16 20:53:23 -0800104 private static final int LOWER_PRIORITY = 4000;
105 private static final int LOWEST_PRIORITY = 0;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800106
107 private final ApplicationId appId;
Hyunsun Moonba290072015-12-16 20:53:23 -0800108 private final FlowRuleService flowRuleService;
109 private final DeviceService deviceService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800110 private final DriverService driverService;
Hyunsun Moonba290072015-12-16 20:53:23 -0800111 private final GroupService groupService;
112 private final MastershipService mastershipService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800113 private final String tunnelType;
114
115 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800116 * Creates a new rule populator.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800117 *
118 * @param appId application id
Hyunsun Moonba290072015-12-16 20:53:23 -0800119 * @param flowRuleService flow rule service
120 * @param deviceService device service
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800121 * @param driverService driver service
122 * @param tunnelType tunnel type
123 */
124 public CordVtnRuleInstaller(ApplicationId appId,
Hyunsun Moonba290072015-12-16 20:53:23 -0800125 FlowRuleService flowRuleService,
126 DeviceService deviceService,
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800127 DriverService driverService,
Hyunsun Moonba290072015-12-16 20:53:23 -0800128 GroupService groupService,
129 MastershipService mastershipService,
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800130 String tunnelType) {
131 this.appId = appId;
Hyunsun Moonba290072015-12-16 20:53:23 -0800132 this.flowRuleService = flowRuleService;
133 this.deviceService = deviceService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800134 this.driverService = driverService;
Hyunsun Moonba290072015-12-16 20:53:23 -0800135 this.groupService = groupService;
136 this.mastershipService = mastershipService;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800137 this.tunnelType = checkNotNull(tunnelType);
138 }
139
140 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800141 * Installs table miss rule to a give device.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800142 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800143 * @param deviceId device id to install the rules
144 * @param tunnelPort tunnel port number of the device
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800145 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800146 public void init(DeviceId deviceId, PortNumber tunnelPort) {
147 // default is drop packets which can be accomplished without
148 // a table miss entry for all table.
149 populateTunnelInPortRule(deviceId, tunnelPort);
150 processAccessTypeTable(deviceId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800151 }
152
153 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800154 * Populates basic rules that connect a VM to the other VMs in the system.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800155 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800156 * @param host host
157 * @param hostIp host ip
158 * @param tunnelIp tunnel ip
159 * @param vNet openstack network
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800160 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800161 public void populateBasicConnectionRules(Host host, IpAddress hostIp, IpAddress tunnelIp,
162 OpenstackNetwork vNet) {
163 // TODO we can get host ip from host.ip() after applying NetworkConfig host provider
164 checkNotNull(host);
165 checkNotNull(vNet);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800166
Hyunsun Moonba290072015-12-16 20:53:23 -0800167 DeviceId deviceId = host.location().deviceId();
168 if (!mastershipService.isLocalMaster(deviceId)) {
169 return;
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800170 }
171
Hyunsun Moonba290072015-12-16 20:53:23 -0800172 PortNumber inPort = host.location().port();
173 MacAddress dstMac = host.mac();
174 long tunnelId = Long.parseLong(vNet.segmentId());
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800175
Hyunsun Moonba290072015-12-16 20:53:23 -0800176 OpenstackSubnet subnet = vNet.subnets().stream()
177 .findFirst()
178 .orElse(null);
179
180 if (subnet == null) {
181 log.error("Failed to get subnet for {}", host.id());
182 return;
183 }
184
185 populateLocalInPortRule(deviceId, inPort, hostIp);
186 populateDirectAccessRule(Ip4Prefix.valueOf(subnet.cidr()), Ip4Prefix.valueOf(subnet.cidr()));
187 populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, tunnelIp);
188 populateTunnelInRule(deviceId, inPort, dstMac, tunnelId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800189 }
190
191 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800192 * Removes basic rules related to a given flow information.
193 *
194 * @param host host to be removed
195 */
196 public void removeBasicConnectionRules(Host host) {
197 checkNotNull(host);
198
199 DeviceId deviceId = host.location().deviceId();
200 MacAddress mac = host.mac();
201 PortNumber port = host.location().port();
202 IpAddress ip = host.ipAddresses().stream().findFirst().orElse(null);
203
204 if (!mastershipService.isLocalMaster(deviceId)) {
205 return;
206 }
207
208 for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
209 if (flowRule.deviceId().equals(deviceId)) {
210 PortNumber inPort = getInPort(flowRule);
211 if (inPort != null && inPort.equals(port)) {
212 processFlowRule(false, flowRule);
213 continue;
214 }
215 }
216
217 MacAddress dstMac = getDstMacFromTreatment(flowRule);
218 if (dstMac != null && dstMac.equals(mac)) {
219 processFlowRule(false, flowRule);
220 continue;
221 }
222
223 dstMac = getDstMacFromSelector(flowRule);
224 if (dstMac != null && dstMac.equals(mac)) {
225 processFlowRule(false, flowRule);
226 continue;
227 }
228
229 IpPrefix dstIp = getDstIpFromSelector(flowRule);
230 if (dstIp != null && dstIp.equals(ip.toIpPrefix())) {
231 processFlowRule(false, flowRule);
232 }
Hyunsun Moonba290072015-12-16 20:53:23 -0800233 }
234
235 // TODO uninstall same network access rule in access table if no vm exists in the network
236 }
237
238 /**
Hyunsun Moonee37c442016-01-07 01:32:31 -0800239 * Populates service dependency rules.
240 *
241 * @param tService tenant cord service
242 * @param pService provider cord service
243 */
244 public void populateServiceDependencyRules(CordService tService, CordService pService) {
245 checkNotNull(tService);
246 checkNotNull(pService);
247
248 Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
249 Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
250 Ip4Address serviceIp = pService.serviceIp().getIp4Address();
251
252 Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
253 Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
254
255 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
256 GroupId groupId = createServiceGroup(device.id(), pService);
257 outGroups.put(device.id(), groupId);
258
259 Set<PortNumber> vms = tService.hosts().keySet()
260 .stream()
261 .filter(host -> host.location().deviceId().equals(device.id()))
262 .map(host -> host.location().port())
263 .collect(Collectors.toSet());
264 inPorts.put(device.id(), vms);
265 }
266
267 populateIndirectAccessRule(srcRange, serviceIp, outGroups);
268 populateDirectAccessRule(srcRange, dstRange);
269 populateInServiceRule(inPorts, outGroups);
270 }
271
272 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800273 * 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 /**
Hyunsun Moonee37c442016-01-07 01:32:31 -0800327 * Updates group buckets for a given service to all devices.
328 *
329 * @param service cord service
330 */
331 public void updateServiceGroup(CordService service) {
332 checkNotNull(service);
333
334 GroupKey groupKey = getGroupKey(service.id());
335
336 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
337 DeviceId deviceId = device.id();
338 if (!mastershipService.isLocalMaster(deviceId)) {
339 continue;
340 }
341
342 Group group = groupService.getGroup(deviceId, groupKey);
343 if (group == null) {
344 log.debug("No group exists for service {} in {}", service.id(), deviceId);
345 continue;
346 }
347
348 List<GroupBucket> oldBuckets = group.buckets().buckets();
349 List<GroupBucket> newBuckets = getServiceGroupBuckets(
350 deviceId, service.segmentationId(), service.hosts()).buckets();
351
352 if (oldBuckets.equals(newBuckets)) {
353 continue;
354 }
355
356 List<GroupBucket> bucketsToRemove = new ArrayList<>(oldBuckets);
357 bucketsToRemove.removeAll(newBuckets);
358 if (!bucketsToRemove.isEmpty()) {
359 groupService.removeBucketsFromGroup(
360 deviceId,
361 groupKey,
362 new GroupBuckets(bucketsToRemove),
363 groupKey, appId);
364 }
365
366 List<GroupBucket> bucketsToAdd = new ArrayList<>(newBuckets);
367 bucketsToAdd.removeAll(oldBuckets);
368 if (!bucketsToAdd.isEmpty()) {
369 groupService.addBucketsToGroup(
370 deviceId,
371 groupKey,
372 new GroupBuckets(bucketsToAdd),
373 groupKey, appId);
374 }
375 }
376 }
377
378 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800379 * Creates a new group for a given service.
380 *
381 * @param deviceId device id to create a group
382 * @param service cord service
383 * @return group id, or null if it fails to create
384 */
385 private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
386 checkNotNull(service);
387
388 GroupKey groupKey = getGroupKey(service.id());
389 Group group = groupService.getGroup(deviceId, groupKey);
390 GroupId groupId = getGroupId(service.id(), deviceId);
391
392 if (group != null) {
393 log.debug("Group {} is already exist in {}", service.id(), deviceId);
394 return groupId;
395 }
396
397 GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
398 GroupDescription groupDescription = new DefaultGroupDescription(
399 deviceId,
400 GroupDescription.Type.SELECT,
401 buckets,
402 groupKey,
403 groupId.id(),
404 appId);
405
406 groupService.addGroup(groupDescription);
407
408 return groupId;
409 }
410
411 /**
412 * Returns group buckets for a given device.
413 *
414 * @param deviceId device id
415 * @param tunnelId tunnel id
416 * @param hosts list of host
417 * @return group buckets
418 */
419 private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
420 List<GroupBucket> buckets = Lists.newArrayList();
421
422 for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
423 Host host = entry.getKey();
424 Ip4Address remoteIp = entry.getValue().getIp4Address();
425 DeviceId hostDevice = host.location().deviceId();
426
427 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
428 .builder()
429 .setEthDst(host.mac());
430
431 if (deviceId.equals(hostDevice)) {
432 tBuilder.setOutput(host.location().port());
433 } else {
434 ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
435 if (tunnelDst == null) {
436 continue;
437 }
438
439 tBuilder.extension(tunnelDst, deviceId)
440 .setTunnelId(tunnelId)
441 .setOutput(getTunnelPort(hostDevice));
442 }
443
444 buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
445 }
446
447 return new GroupBuckets(buckets);
448 }
449
450 /**
451 * Returns globally unique group ID.
452 *
453 * @param serviceId service id
454 * @param deviceId device id
455 * @return group id
456 */
457 private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
458 return new DefaultGroupId(Objects.hash(serviceId, deviceId));
459 }
460
461 /**
462 * Returns group key of a service.
463 *
464 * @param serviceId service id
465 * @return group key
466 */
467 private GroupKey getGroupKey(CordServiceId serviceId) {
468 return new DefaultGroupKey(serviceId.id().getBytes());
469 }
470
471 /**
472 * Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
473 *
474 * @param deviceId device id
475 */
476 private void processAccessTypeTable(DeviceId deviceId) {
477 TrafficSelector selector = DefaultTrafficSelector.builder()
478 .build();
479
480 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
481 .transition(TABLE_IN_SERVICE)
482 .build();
483
484 FlowRule flowRule = DefaultFlowRule.builder()
485 .fromApp(appId)
486 .withSelector(selector)
487 .withTreatment(treatment)
488 .withPriority(LOWEST_PRIORITY)
489 .forDevice(deviceId)
490 .forTable(TABLE_ACCESS_TYPE)
491 .makePermanent()
492 .build();
493
494 processFlowRule(true, flowRule);
495 }
496
497 /**
498 * Populates rules for tunnel flows in port in IN_PORT table.
499 * All flows from tunnel port are forwarded to TUNNEL_ID table.
500 *
501 * @param deviceId device id to install the rules
502 * @param tunnelPort tunnel port
503 */
504 private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
505 checkNotNull(tunnelPort);
506
507 TrafficSelector selector = DefaultTrafficSelector.builder()
508 .matchInPort(tunnelPort)
509 .build();
510 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
511 .transition(TABLE_TUNNEL_IN)
512 .build();
513
514 FlowRule flowRule = DefaultFlowRule.builder()
515 .fromApp(appId)
516 .withSelector(selector)
517 .withTreatment(treatment)
518 .withPriority(DEFAULT_PRIORITY)
519 .forDevice(deviceId)
520 .forTable(TABLE_IN_PORT)
521 .makePermanent()
522 .build();
523
524 processFlowRule(true, flowRule);
525 }
526
527 /**
528 * Populates rules for local in port in IN_PORT table.
529 * Flows from a given in port, whose source IP is service IP transition
530 * to DST_TYPE table. Other flows transition to IN_SERVICE table.
531 *
532 * @param deviceId device id to install the rules
533 * @param inPort in port
534 * @param srcIp source ip
535 */
536 private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp) {
537 TrafficSelector selector = DefaultTrafficSelector.builder()
538 .matchInPort(inPort)
539 .matchEthType(Ethernet.TYPE_IPV4)
540 .matchIPSrc(srcIp.toIpPrefix())
541 .build();
542 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
543 .transition(TABLE_ACCESS_TYPE)
544 .build();
545
546
547 FlowRule flowRule = DefaultFlowRule.builder()
548 .fromApp(appId)
549 .withSelector(selector)
550 .withTreatment(treatment)
551 .withPriority(DEFAULT_PRIORITY)
552 .forDevice(deviceId)
553 .forTable(TABLE_IN_PORT)
554 .makePermanent()
555 .build();
556
557 processFlowRule(true, flowRule);
558
559 selector = DefaultTrafficSelector.builder()
560 .matchInPort(inPort)
561 .build();
562 treatment = DefaultTrafficTreatment.builder()
563 .transition(TABLE_IN_SERVICE)
564 .build();
565
566 flowRule = DefaultFlowRule.builder()
567 .fromApp(appId)
568 .withSelector(selector)
569 .withTreatment(treatment)
570 .withPriority(LOWER_PRIORITY)
571 .forDevice(deviceId)
572 .forTable(TABLE_IN_PORT)
573 .makePermanent()
574 .build();
575
576 processFlowRule(true, flowRule);
577 }
578
579 /**
580 * Populates direct VM access rules for ACCESS_TYPE table.
581 * These rules are installed to all devices.
582 *
583 * @param srcRange source ip range
584 * @param dstRange destination ip range
585 */
586 private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange) {
587 TrafficSelector selector = DefaultTrafficSelector.builder()
588 .matchEthType(Ethernet.TYPE_IPV4)
589 .matchIPSrc(srcRange)
590 .matchIPDst(dstRange)
591 .build();
592 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
593 .transition(TABLE_DST_IP)
594 .build();
595
596 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
597 FlowRule flowRuleDirect = DefaultFlowRule.builder()
598 .fromApp(appId)
599 .withSelector(selector)
600 .withTreatment(treatment)
601 .withPriority(LOWER_PRIORITY)
602 .forDevice(device.id())
603 .forTable(TABLE_ACCESS_TYPE)
604 .makePermanent()
605 .build();
606
607 processFlowRule(true, flowRuleDirect);
608 }
609 }
610
611 /**
612 * Populates indirect service access rules for ACCESS_TYPE table.
613 * These rules are installed to all devices.
614 *
615 * @param srcRange source range
616 * @param serviceIp service ip
617 * @param outGroups list of output group
618 */
619 private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
620 Map<DeviceId, GroupId> outGroups) {
621 TrafficSelector selector = DefaultTrafficSelector.builder()
622 .matchEthType(Ethernet.TYPE_IPV4)
623 .matchIPSrc(srcRange)
624 .matchIPDst(serviceIp.toIpPrefix())
625 .build();
626
627 for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
628 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
629 .group(outGroup.getValue())
630 .build();
631
632 FlowRule flowRule = DefaultFlowRule.builder()
633 .fromApp(appId)
634 .withSelector(selector)
635 .withTreatment(treatment)
636 .withPriority(DEFAULT_PRIORITY)
637 .forDevice(outGroup.getKey())
638 .forTable(TABLE_ACCESS_TYPE)
639 .makePermanent()
640 .build();
641
642 processFlowRule(true, flowRule);
643 }
644 }
645
646 /**
647 * Populates flow rules for IN_SERVICE table.
648 *
649 * @param inPorts list of inports related to the service for each device
650 * @param outGroups set of output groups
651 */
652 private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts, Map<DeviceId, GroupId> outGroups) {
653 checkNotNull(inPorts);
654 checkNotNull(outGroups);
655
656 for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
657 Set<PortNumber> ports = entry.getValue();
658 DeviceId deviceId = entry.getKey();
659
660 GroupId groupId = outGroups.get(deviceId);
661 if (groupId == null) {
662 continue;
663 }
664
665 ports.stream().forEach(port -> {
666 TrafficSelector selector = DefaultTrafficSelector.builder()
667 .matchInPort(port)
668 .build();
669 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
670 .group(groupId)
671 .build();
672
673 FlowRule flowRule = DefaultFlowRule.builder()
674 .fromApp(appId)
675 .withSelector(selector)
676 .withTreatment(treatment)
677 .withPriority(DEFAULT_PRIORITY)
678 .forDevice(deviceId)
679 .forTable(TABLE_IN_SERVICE)
680 .makePermanent()
681 .build();
682
683 processFlowRule(true, flowRule);
684 });
685 }
686 }
687
688 /**
689 * Populates flow rules for DST_IP table.
690 *
691 * @param deviceId device id
692 * @param inPort in port
693 * @param dstMac mac address
694 * @param dstIp destination ip
695 * @param tunnelId tunnel id
696 * @param tunnelIp tunnel remote ip
697 */
698 private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
699 IpAddress dstIp, long tunnelId, IpAddress tunnelIp) {
700 TrafficSelector selector = DefaultTrafficSelector.builder()
701 .matchEthType(Ethernet.TYPE_IPV4)
702 .matchIPDst(dstIp.toIpPrefix())
703 .build();
704 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
705 .setEthDst(dstMac)
706 .setOutput(inPort)
707 .build();
708
709 FlowRule flowRule = DefaultFlowRule.builder()
710 .fromApp(appId)
711 .withSelector(selector)
712 .withTreatment(treatment)
713 .withPriority(DEFAULT_PRIORITY)
714 .forDevice(deviceId)
715 .forTable(TABLE_DST_IP)
716 .makePermanent()
717 .build();
718
719 processFlowRule(true, flowRule);
720
721 for (Device device : deviceService.getAvailableDevices(SWITCH)) {
722 if (device.id().equals(deviceId)) {
723 continue;
724 }
725
726 ExtensionTreatment tunnelDst = getTunnelDst(device.id(), tunnelIp.getIp4Address());
727 if (tunnelDst == null) {
728 continue;
729 }
730
731 treatment = DefaultTrafficTreatment.builder()
732 .setEthDst(dstMac)
733 .setTunnelId(tunnelId)
734 .extension(tunnelDst, device.id())
735 .setOutput(getTunnelPort(device.id()))
736 .build();
737
738 flowRule = DefaultFlowRule.builder()
739 .fromApp(appId)
740 .withSelector(selector)
741 .withTreatment(treatment)
742 .withPriority(DEFAULT_PRIORITY)
743 .forDevice(device.id())
744 .forTable(TABLE_DST_IP)
745 .makePermanent()
746 .build();
747
748 processFlowRule(true, flowRule);
749 }
750 }
751
752 /**
753 * Populates flow rules for TUNNEL_ID table.
754 *
755 * @param deviceId device id
756 * @param inPort in port
757 * @param mac mac address
758 * @param tunnelId tunnel id
759 */
760 private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac, long tunnelId) {
761 TrafficSelector selector = DefaultTrafficSelector.builder()
762 .matchTunnelId(tunnelId)
763 .matchEthDst(mac)
764 .build();
765
766 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
767 .setOutput(inPort)
768 .build();
769
770 FlowRule flowRule = DefaultFlowRule.builder()
771 .fromApp(appId)
772 .withSelector(selector)
773 .withTreatment(treatment)
774 .withPriority(DEFAULT_PRIORITY)
775 .forDevice(deviceId)
776 .forTable(TABLE_TUNNEL_IN)
777 .makePermanent()
778 .build();
779
780 processFlowRule(true, flowRule);
781 }
782
783 /**
784 * Installs or uninstall a given rule.
785 *
786 * @param install true to install, false to uninstall
787 * @param rule rule
788 */
789 private void processFlowRule(boolean install, FlowRule rule) {
790 FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
791 oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
792
793 flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
794 @Override
795 public void onError(FlowRuleOperations ops) {
796 log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
797 }
798 }));
799 }
800
801 /**
802 * Returns tunnel port of the device.
803 *
804 * @param deviceId device id
805 * @return tunnel port number, or null if no tunnel port exists on a given device
806 */
807 private PortNumber getTunnelPort(DeviceId deviceId) {
808 try {
809 return deviceService.getPorts(deviceId).stream()
810 .filter(p -> p.annotations().value("portName").contains(tunnelType))
811 .findFirst().get().number();
812 } catch (NoSuchElementException e) {
813 return null;
814 }
815 }
816
817 /**
818 * Returns the inport from a given flow rule if the rule contains the match of it.
819 *
820 * @param flowRule flow rule
821 * @return port number, or null if the rule doesn't have inport match
822 */
823 private PortNumber getInPort(FlowRule flowRule) {
824 Criterion criterion = flowRule.selector().getCriterion(IN_PORT);
825 if (criterion != null && criterion instanceof PortCriterion) {
826 PortCriterion port = (PortCriterion) criterion;
827 return port.port();
828 } else {
829 return null;
830 }
831 }
832
833 /**
834 * Returns the destination mac address from a given flow rule if the rule
835 * contains the instruction of it.
836 *
837 * @param flowRule flow rule
838 * @return mac address, or null if the rule doesn't have destination mac instruction
839 */
840 private MacAddress getDstMacFromTreatment(FlowRule flowRule) {
841 Instruction instruction = flowRule.treatment().allInstructions().stream()
842 .filter(inst -> inst instanceof ModEtherInstruction &&
843 ((ModEtherInstruction) inst).subtype().equals(ETH_DST))
844 .findFirst()
845 .orElse(null);
846
847 if (instruction == null) {
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800848 return null;
849 }
850
Hyunsun Moonba290072015-12-16 20:53:23 -0800851 return ((ModEtherInstruction) instruction).mac();
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800852 }
853
854 /**
Hyunsun Moonba290072015-12-16 20:53:23 -0800855 * Returns the destination mac address from a given flow rule if the rule
856 * contains the match of it.
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800857 *
Hyunsun Moonba290072015-12-16 20:53:23 -0800858 * @param flowRule flow rule
859 * @return mac address, or null if the rule doesn't have destination mac match
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800860 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800861 private MacAddress getDstMacFromSelector(FlowRule flowRule) {
862 Criterion criterion = flowRule.selector().getCriterion(Criterion.Type.ETH_DST);
863 if (criterion != null && criterion instanceof EthCriterion) {
864 EthCriterion eth = (EthCriterion) criterion;
865 return eth.mac();
866 } else {
867 return null;
868 }
869 }
870
871 /**
872 * Returns the destination IP from a given flow rule if the rule contains
873 * the match of it.
874 *
875 * @param flowRule flow rule
876 * @return ip prefix, or null if the rule doesn't have ip match
877 */
878 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
879 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
880 if (criterion != null && criterion instanceof IPCriterion) {
881 IPCriterion ip = (IPCriterion) criterion;
882 return ip.ip();
883 } else {
884 return null;
885 }
886 }
887
888 /**
889 * Returns the source IP from a given flow rule if the rule contains
890 * the match of it.
891 *
892 * @param flowRule flow rule
893 * @return ip prefix, or null if the rule doesn't have ip match
894 */
895 private IpPrefix getSrcIpFromSelector(FlowRule flowRule) {
896 Criterion criterion = flowRule.selector().getCriterion(IPV4_SRC);
897 if (criterion != null && criterion instanceof IPCriterion) {
898 IPCriterion ip = (IPCriterion) criterion;
899 return ip.ip();
900 } else {
901 return null;
902 }
903 }
904
905 /**
906 * Returns the group ID from a given flow rule if the rule contains the
907 * treatment of it.
908 *
909 * @param flowRule flow rule
910 * @return group id, or null if the rule doesn't have group instruction
911 */
912 private GroupId getGroupIdFromTreatment(FlowRule flowRule) {
913 Instruction instruction = flowRule.treatment().allInstructions().stream()
914 .filter(inst -> inst instanceof Instructions.GroupInstruction)
915 .findFirst()
916 .orElse(null);
917
918 if (instruction == null) {
919 return null;
920 }
921
922 return ((Instructions.GroupInstruction) instruction).groupId();
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800923 }
924
925 /**
926 * Returns extension instruction to set tunnel destination.
927 *
928 * @param deviceId device id
929 * @param remoteIp tunnel destination address
930 * @return extension treatment or null if it fails to get instruction
931 */
Hyunsun Moonba290072015-12-16 20:53:23 -0800932 private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800933 try {
934 Driver driver = driverService.getDriver(deviceId);
Hyunsun Moonba290072015-12-16 20:53:23 -0800935 DefaultDriverData driverData = new DefaultDriverData(driver, deviceId);
936 DriverHandler handler = new DefaultDriverHandler(driverData);
937 ExtensionTreatmentResolver resolver = handler.behaviour(ExtensionTreatmentResolver.class);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800938
Hyunsun Moonba290072015-12-16 20:53:23 -0800939 ExtensionTreatment treatment =
940 resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800941 treatment.setPropertyValue("tunnelDst", remoteIp);
942
943 return treatment;
Hyunsun Moonba290072015-12-16 20:53:23 -0800944 } catch (ItemNotFoundException | UnsupportedOperationException |
945 ExtensionPropertyException e) {
946 log.error("Failed to get extension instruction {}", deviceId);
Hyunsun Moon05f528a2015-11-04 17:34:35 -0800947 return null;
948 }
949 }
950}
Hyunsun Moonba290072015-12-16 20:53:23 -0800951