2 * Copyright 2015 Open Networking Laboratory
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.onosproject.segmentrouting;
18 import org.onlab.packet.Ethernet;
19 import org.onlab.packet.Ip4Address;
20 import org.onlab.packet.Ip4Prefix;
21 import org.onlab.packet.IpPrefix;
22 import org.onlab.packet.MacAddress;
23 import org.onlab.packet.MplsLabel;
24 import org.onlab.packet.VlanId;
25 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
26 import org.onosproject.segmentrouting.config.DeviceConfiguration;
27 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
28 import org.onosproject.net.DeviceId;
29 import org.onosproject.net.Link;
30 import org.onosproject.net.Port;
31 import org.onosproject.net.PortNumber;
32 import org.onosproject.net.flow.DefaultTrafficSelector;
33 import org.onosproject.net.flow.DefaultTrafficTreatment;
34 import org.onosproject.net.flow.TrafficSelector;
35 import org.onosproject.net.flow.TrafficTreatment;
36 import org.onosproject.net.flow.criteria.Criteria;
37 import org.onosproject.net.flowobjective.DefaultFilteringObjective;
38 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
39 import org.onosproject.net.flowobjective.FilteringObjective;
40 import org.onosproject.net.flowobjective.ForwardingObjective;
41 import org.onosproject.net.flowobjective.Objective;
42 import org.onosproject.net.flowobjective.ObjectiveError;
43 import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
44 import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
45 import org.onosproject.net.flowobjective.ObjectiveContext;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import java.util.ArrayList;
50 import java.util.HashSet;
51 import java.util.List;
53 import java.util.concurrent.atomic.AtomicLong;
55 import static com.google.common.base.Preconditions.checkNotNull;
57 public class RoutingRulePopulator {
59 private static final Logger log = LoggerFactory
60 .getLogger(RoutingRulePopulator.class);
62 private AtomicLong rulePopulationCounter;
63 private SegmentRoutingManager srManager;
64 private DeviceConfiguration config;
66 private static final int HIGHEST_PRIORITY = 0xffff;
67 private static final long OFPP_MAX = 0xffffff00L;
71 * Creates a RoutingRulePopulator object.
73 * @param srManager segment routing manager reference
75 public RoutingRulePopulator(SegmentRoutingManager srManager) {
76 this.srManager = srManager;
77 this.config = checkNotNull(srManager.deviceConfiguration);
78 this.rulePopulationCounter = new AtomicLong(0);
82 * Resets the population counter.
84 public void resetCounter() {
85 rulePopulationCounter.set(0);
89 * Returns the number of rules populated.
91 * @return number of rules
93 public long getCounter() {
94 return rulePopulationCounter.get();
98 * Populates IP flow rules for specific hosts directly connected to the
101 * @param deviceId switch ID to set the rules
102 * @param hostIp host IP address
103 * @param hostMac host MAC address
104 * @param outPort port where the host is connected
106 public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
107 MacAddress hostMac, PortNumber outPort) {
108 MacAddress deviceMac;
110 deviceMac = config.getDeviceMac(deviceId);
111 } catch (DeviceConfigNotFoundException e) {
112 log.warn(e.getMessage() + " Aborting populateIpRuleForHost.");
116 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
117 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
119 sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH));
120 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
124 .setEthSrc(deviceMac)
127 TrafficTreatment treatment = tbuilder.build();
128 TrafficSelector selector = sbuilder.build();
130 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
131 .builder().fromApp(srManager.appId).makePermanent()
132 .withSelector(selector).withTreatment(treatment)
133 .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
135 log.debug("Installing IPv4 forwarding objective "
136 + "for host {} in switch {}", hostIp, deviceId);
137 srManager.flowObjectiveService.
140 add(new SRObjectiveContext(deviceId,
141 SRObjectiveContext.ObjectiveType.FORWARDING)));
142 rulePopulationCounter.incrementAndGet();
146 * Populates IP flow rules for the subnets of the destination router.
148 * @param deviceId switch ID to set the rules
149 * @param subnets subnet information
150 * @param destSw destination switch ID
151 * @param nextHops next hop switch ID list
152 * @return true if all rules are set successfully, false otherwise
154 public boolean populateIpRuleForSubnet(DeviceId deviceId,
155 Set<Ip4Prefix> subnets,
157 Set<DeviceId> nextHops) {
159 for (IpPrefix subnet : subnets) {
160 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
169 * Populates IP flow rules for the router IP address.
171 * @param deviceId target device ID to set the rules
172 * @param ipPrefix the IP address of the destination router
173 * @param destSw device ID of the destination router
174 * @param nextHops next hop switch ID list
175 * @return true if all rules are set successfully, false otherwise
177 public boolean populateIpRuleForRouter(DeviceId deviceId,
178 IpPrefix ipPrefix, DeviceId destSw,
179 Set<DeviceId> nextHops) {
182 segmentId = config.getSegmentId(destSw);
183 } catch (DeviceConfigNotFoundException e) {
184 log.warn(e.getMessage() + " Aborting populateIpRuleForRouter.");
188 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
189 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
191 sbuilder.matchIPDst(ipPrefix);
192 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
194 NeighborSet ns = null;
196 // If the next hop is the same as the final destination, then MPLS label
198 if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
199 tbuilder.deferred().decNwTtl();
200 ns = new NeighborSet(nextHops);
202 tbuilder.deferred().copyTtlOut();
203 ns = new NeighborSet(nextHops, segmentId);
206 TrafficTreatment treatment = tbuilder.build();
207 TrafficSelector selector = sbuilder.build();
209 if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
210 log.warn("No next objective in {} for ns: {}", deviceId, ns);
214 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
216 .fromApp(srManager.appId)
218 .nextStep(srManager.getNextObjectiveId(deviceId, ns))
219 .withTreatment(treatment)
220 .withSelector(selector)
222 .withFlag(ForwardingObjective.Flag.SPECIFIC);
223 log.debug("Installing IPv4 forwarding objective "
224 + "for router IP/subnet {} in switch {}",
227 srManager.flowObjectiveService.
230 add(new SRObjectiveContext(deviceId,
231 SRObjectiveContext.ObjectiveType.FORWARDING)));
232 rulePopulationCounter.incrementAndGet();
238 * Populates MPLS flow rules to all routers.
240 * @param deviceId target device ID of the switch to set the rules
241 * @param destSwId destination switch device ID
242 * @param nextHops next hops switch ID list
243 * @return true if all rules are set successfully, false otherwise
245 public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId,
246 Set<DeviceId> nextHops) {
249 segmentId = config.getSegmentId(destSwId);
250 } catch (DeviceConfigNotFoundException e) {
251 log.warn(e.getMessage() + " Aborting populateMplsRule.");
255 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
256 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
258 // TODO Handle the case of Bos == false
259 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
260 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
262 // If the next hop is the destination router, do PHP
263 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
264 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
265 + "label {} in switch {} with PHP",
269 ForwardingObjective.Builder fwdObjBosBuilder =
270 getMplsForwardingObjective(deviceId,
275 // TODO: Check with Sangho on why we need this
276 ForwardingObjective.Builder fwdObjNoBosBuilder =
277 getMplsForwardingObjective(deviceId,
282 if (fwdObjBosBuilder != null) {
283 fwdObjBuilders.add(fwdObjBosBuilder);
285 log.warn("Failed to set MPLS rules.");
289 log.debug("Installing MPLS forwarding objective for "
290 + "label {} in switch {} without PHP",
294 ForwardingObjective.Builder fwdObjBosBuilder =
295 getMplsForwardingObjective(deviceId,
300 // TODO: Check with Sangho on why we need this
301 ForwardingObjective.Builder fwdObjNoBosBuilder =
302 getMplsForwardingObjective(deviceId,
307 if (fwdObjBosBuilder != null) {
308 fwdObjBuilders.add(fwdObjBosBuilder);
310 log.warn("Failed to set MPLS rules.");
315 TrafficSelector selector = sbuilder.build();
316 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
317 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
318 .makePermanent()).withSelector(selector)
320 .withFlag(ForwardingObjective.Flag.SPECIFIC);
321 srManager.flowObjectiveService.
324 add(new SRObjectiveContext(deviceId,
325 SRObjectiveContext.ObjectiveType.FORWARDING)));
326 rulePopulationCounter.incrementAndGet();
332 private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId,
334 Set<DeviceId> nextHops,
337 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
338 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
339 DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
345 isEdge = config.isEdgeDevice(deviceId);
346 srcMac = config.getDeviceMac(deviceId);
347 dstMac = config.getDeviceMac(nextHop);
348 } catch (DeviceConfigNotFoundException e) {
349 log.warn(e.getMessage() + " Aborting getMplsForwardingObjective");
353 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
356 log.debug("getMplsForwardingObjective: php required");
357 tbuilder.deferred().copyTtlIn();
359 tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl();
361 tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
364 log.debug("getMplsForwardingObjective: php not required");
365 tbuilder.deferred().decMplsTtl();
368 if (!isECMPSupportedInTransitRouter() && !isEdge) {
369 PortNumber port = selectOnePort(deviceId, nextHops);
371 log.warn("No link from {} to {}", deviceId, nextHops);
378 fwdBuilder.withTreatment(tbuilder.build());
380 NeighborSet ns = new NeighborSet(nextHops);
381 fwdBuilder.withTreatment(tbuilder.build());
382 fwdBuilder.nextStep(srManager
383 .getNextObjectiveId(deviceId, ns));
389 private boolean isECMPSupportedInTransitRouter() {
391 // TODO: remove this function when objectives subsystem is supported.
396 * Creates a filtering objective to permit all untagged packets with a
397 * dstMac corresponding to the router's MAC address. For those pipelines
398 * that need to internally assign vlans to untagged packets, this method
399 * provides per-subnet vlan-ids as metadata.
401 * Note that the vlan assignment is only done by the master-instance for a switch.
402 * However we send the filtering objective from slave-instances as well, so
403 * that drivers can obtain other information (like Router MAC and IP).
405 * @param deviceId the switch dpid for the router
407 public void populateRouterMacVlanFilters(DeviceId deviceId) {
408 log.debug("Installing per-port filtering objective for untagged "
409 + "packets in device {}", deviceId);
411 MacAddress deviceMac;
413 deviceMac = config.getDeviceMac(deviceId);
414 } catch (DeviceConfigNotFoundException e) {
415 log.warn(e.getMessage() + " Aborting populateRouterMacVlanFilters.");
419 for (Port port : srManager.deviceService.getPorts(deviceId)) {
420 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
421 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
422 VlanId assignedVlan = (portSubnet == null)
423 ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
424 : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
428 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
429 fob.withKey(Criteria.matchInPort(port.number()))
430 .addCondition(Criteria.matchEthDst(deviceMac))
431 .addCondition(Criteria.matchVlanId(VlanId.NONE));
432 // vlan assignment is valid only if this instance is master
433 if (srManager.mastershipService.isLocalMaster(deviceId)) {
434 TrafficTreatment tt = DefaultTrafficTreatment.builder()
435 .pushVlan().setVlanId(assignedVlan).build();
438 fob.permit().fromApp(srManager.appId);
439 srManager.flowObjectiveService.
440 filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
441 SRObjectiveContext.ObjectiveType.FILTER)));
447 * Creates a forwarding objective to punt all IP packets, destined to the
448 * router's port IP addresses, to the controller. Note that the input
449 * port should not be matched on, as these packets can come from any input.
450 * Furthermore, these are applied only by the master instance.
452 * @param deviceId the switch dpid for the router
454 public void populateRouterIpPunts(DeviceId deviceId) {
457 routerIp = config.getRouterIp(deviceId);
458 } catch (DeviceConfigNotFoundException e) {
459 log.warn(e.getMessage() + " Aborting populateRouterIpPunts.");
463 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
464 log.debug("Not installing port-IP punts - not the master for dev:{} ",
468 ForwardingObjective.Builder puntIp = DefaultForwardingObjective.builder();
469 Set<Ip4Address> allIps = new HashSet<Ip4Address>(config.getPortIPs(deviceId));
470 allIps.add(routerIp);
471 for (Ip4Address ipaddr : allIps) {
472 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
473 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
474 selector.matchEthType(Ethernet.TYPE_IPV4);
475 selector.matchIPDst(IpPrefix.valueOf(ipaddr,
476 IpPrefix.MAX_INET_MASK_LENGTH));
477 treatment.setOutput(PortNumber.CONTROLLER);
478 puntIp.withSelector(selector.build());
479 puntIp.withTreatment(treatment.build());
480 puntIp.withFlag(Flag.VERSATILE)
481 .withPriority(HIGHEST_PRIORITY)
483 .fromApp(srManager.appId);
484 log.debug("Installing forwarding objective to punt port IP addresses");
485 srManager.flowObjectiveService.
487 puntIp.add(new SRObjectiveContext(deviceId,
488 SRObjectiveContext.ObjectiveType.FORWARDING)));
492 private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
494 Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
495 for (DeviceId destId: destIds) {
496 for (Link link : links) {
497 if (link.dst().deviceId().equals(destId)) {
498 return link.src().port();
499 } else if (link.src().deviceId().equals(destId)) {
500 return link.dst().port();
508 private static class SRObjectiveContext implements ObjectiveContext {
513 final DeviceId deviceId;
514 final ObjectiveType type;
516 SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
517 this.deviceId = deviceId;
521 public void onSuccess(Objective objective) {
522 log.debug("{} objective operation successful in device {}",
523 type.name(), deviceId);
527 public void onError(Objective objective, ObjectiveError error) {
528 log.warn("{} objective {} operation failed with error: {} in device {}",
529 type.name(), objective, error, deviceId);