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.grouphandler.NeighborSet;
26 import org.onosproject.net.DeviceId;
27 import org.onosproject.net.Link;
28 import org.onosproject.net.PortNumber;
29 import org.onosproject.net.flow.DefaultTrafficSelector;
30 import org.onosproject.net.flow.DefaultTrafficTreatment;
31 import org.onosproject.net.flow.TrafficSelector;
32 import org.onosproject.net.flow.TrafficTreatment;
33 import org.onosproject.net.flow.criteria.Criteria;
34 import org.onosproject.net.flowobjective.DefaultFilteringObjective;
35 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
36 import org.onosproject.net.flowobjective.FilteringObjective;
37 import org.onosproject.net.flowobjective.ForwardingObjective;
38 import org.onosproject.net.flowobjective.Objective;
39 import org.onosproject.net.flowobjective.ObjectiveError;
40 import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
41 import org.onosproject.net.flowobjective.ObjectiveContext;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
45 import java.util.ArrayList;
46 import java.util.List;
48 import java.util.concurrent.atomic.AtomicLong;
50 import static com.google.common.base.Preconditions.checkNotNull;
52 public class RoutingRulePopulator {
54 private static final Logger log = LoggerFactory
55 .getLogger(RoutingRulePopulator.class);
57 private AtomicLong rulePopulationCounter;
58 private SegmentRoutingManager srManager;
59 private DeviceConfiguration config;
61 * Creates a RoutingRulePopulator object.
63 * @param srManager segment routing manager reference
65 public RoutingRulePopulator(SegmentRoutingManager srManager) {
66 this.srManager = srManager;
67 this.config = checkNotNull(srManager.deviceConfiguration);
68 this.rulePopulationCounter = new AtomicLong(0);
72 * Resets the population counter.
74 public void resetCounter() {
75 rulePopulationCounter.set(0);
79 * Returns the number of rules populated.
81 * @return number of rules
83 public long getCounter() {
84 return rulePopulationCounter.get();
88 * Populates IP flow rules for specific hosts directly connected to the
91 * @param deviceId switch ID to set the rules
92 * @param hostIp host IP address
93 * @param hostMac host MAC address
94 * @param outPort port where the host is connected
96 public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
97 MacAddress hostMac, PortNumber outPort) {
98 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
99 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
101 sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32));
102 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
106 .setEthSrc(config.getDeviceMac(deviceId))
109 TrafficTreatment treatment = tbuilder.build();
110 TrafficSelector selector = sbuilder.build();
112 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
113 .builder().fromApp(srManager.appId).makePermanent()
114 .withSelector(selector).withTreatment(treatment)
115 .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
117 log.debug("Installing IPv4 forwarding objective "
118 + "for host {} in switch {}", hostIp, deviceId);
119 srManager.flowObjectiveService.
122 add(new SRObjectiveContext(deviceId,
123 SRObjectiveContext.ObjectiveType.FORWARDING)));
124 rulePopulationCounter.incrementAndGet();
128 * Populates IP flow rules for the subnets of the destination router.
130 * @param deviceId switch ID to set the rules
131 * @param subnets subnet information
132 * @param destSw destination switch ID
133 * @param nextHops next hop switch ID list
134 * @return true if all rules are set successfully, false otherwise
136 public boolean populateIpRuleForSubnet(DeviceId deviceId,
137 List<Ip4Prefix> subnets,
139 Set<DeviceId> nextHops) {
141 for (IpPrefix subnet : subnets) {
142 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
151 * Populates IP flow rules for the router IP address.
153 * @param deviceId target device ID to set the rules
154 * @param ipPrefix the IP address of the destination router
155 * @param destSw device ID of the destination router
156 * @param nextHops next hop switch ID list
157 * @return true if all rules are set successfully, false otherwise
159 public boolean populateIpRuleForRouter(DeviceId deviceId,
160 IpPrefix ipPrefix, DeviceId destSw,
161 Set<DeviceId> nextHops) {
163 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
164 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
166 sbuilder.matchIPDst(ipPrefix);
167 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
169 NeighborSet ns = null;
171 // If the next hop is the same as the final destination, then MPLS label
173 if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
174 tbuilder.deferred().decNwTtl();
175 ns = new NeighborSet(nextHops);
177 tbuilder.deferred().copyTtlOut();
178 ns = new NeighborSet(nextHops, config.getSegmentId(destSw));
181 TrafficTreatment treatment = tbuilder.build();
182 TrafficSelector selector = sbuilder.build();
184 if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
185 log.warn("No next objective in {} for ns: {}", deviceId, ns);
189 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
191 .fromApp(srManager.appId)
193 .nextStep(srManager.getNextObjectiveId(deviceId, ns))
194 .withTreatment(treatment)
195 .withSelector(selector)
197 .withFlag(ForwardingObjective.Flag.SPECIFIC);
198 log.debug("Installing IPv4 forwarding objective "
199 + "for router IP/subnet {} in switch {}",
202 srManager.flowObjectiveService.
205 add(new SRObjectiveContext(deviceId,
206 SRObjectiveContext.ObjectiveType.FORWARDING)));
207 rulePopulationCounter.incrementAndGet();
213 * Populates MPLS flow rules to all routers.
215 * @param deviceId target device ID of the switch to set the rules
216 * @param destSwId destination switch device ID
217 * @param nextHops next hops switch ID list
218 * @return true if all rules are set successfully, false otherwise
220 public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId,
221 Set<DeviceId> nextHops) {
223 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
224 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
226 // TODO Handle the case of Bos == false
227 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getSegmentId(destSwId)));
228 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
230 // If the next hop is the destination router, do PHP
231 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
232 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
233 + "label {} in switch {} with PHP",
234 config.getSegmentId(destSwId),
237 ForwardingObjective.Builder fwdObjBosBuilder =
238 getMplsForwardingObjective(deviceId,
243 // TODO: Check with Sangho on why we need this
244 ForwardingObjective.Builder fwdObjNoBosBuilder =
245 getMplsForwardingObjective(deviceId,
250 if (fwdObjBosBuilder != null) {
251 fwdObjBuilders.add(fwdObjBosBuilder);
253 log.warn("Failed to set MPLS rules.");
257 log.debug("Installing MPLS forwarding objective for "
258 + "label {} in switch {} without PHP",
259 config.getSegmentId(destSwId),
262 ForwardingObjective.Builder fwdObjBosBuilder =
263 getMplsForwardingObjective(deviceId,
268 // TODO: Check with Sangho on why we need this
269 ForwardingObjective.Builder fwdObjNoBosBuilder =
270 getMplsForwardingObjective(deviceId,
275 if (fwdObjBosBuilder != null) {
276 fwdObjBuilders.add(fwdObjBosBuilder);
278 log.warn("Failed to set MPLS rules.");
283 TrafficSelector selector = sbuilder.build();
284 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
285 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
286 .makePermanent()).withSelector(selector)
288 .withFlag(ForwardingObjective.Flag.SPECIFIC);
289 srManager.flowObjectiveService.
292 add(new SRObjectiveContext(deviceId,
293 SRObjectiveContext.ObjectiveType.FORWARDING)));
294 rulePopulationCounter.incrementAndGet();
300 private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId,
302 Set<DeviceId> nextHops,
306 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
307 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
309 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
312 log.debug("getMplsForwardingObjective: php required");
313 tbuilder.deferred().copyTtlIn();
315 tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl();
317 tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
320 log.debug("getMplsForwardingObjective: php not required");
321 tbuilder.deferred().decMplsTtl();
324 if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) {
325 PortNumber port = selectOnePort(deviceId, nextHops);
326 DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
328 log.warn("No link from {} to {}", deviceId, nextHops);
332 .setEthSrc(config.getDeviceMac(deviceId))
333 .setEthDst(config.getDeviceMac(nextHop))
335 fwdBuilder.withTreatment(tbuilder.build());
337 NeighborSet ns = new NeighborSet(nextHops);
338 fwdBuilder.withTreatment(tbuilder.build());
339 fwdBuilder.nextStep(srManager
340 .getNextObjectiveId(deviceId, ns));
346 private boolean isECMPSupportedInTransitRouter() {
348 // TODO: remove this function when objectives subsystem is supported.
353 * Populates VLAN flows rules. All packets are forwarded to TMAC table.
355 * @param deviceId switch ID to set the rules
357 public void populateTableVlan(DeviceId deviceId) {
358 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
359 fob.withKey(Criteria.matchInPort(PortNumber.ALL))
360 .addCondition(Criteria.matchVlanId(VlanId.NONE));
361 fob.permit().fromApp(srManager.appId);
362 log.debug("populateTableVlan: Installing filtering objective for untagged packets");
363 srManager.flowObjectiveService.
365 fob.add(new SRObjectiveContext(deviceId,
366 SRObjectiveContext.ObjectiveType.FILTER)));
370 * Populates TMAC table rules. IP packets are forwarded to IP table. MPLS
371 * packets are forwarded to MPLS table.
373 * @param deviceId switch ID to set the rules
375 public void populateTableTMac(DeviceId deviceId) {
377 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
378 fob.withKey(Criteria.matchInPort(PortNumber.ALL))
379 .addCondition(Criteria.matchEthDst(config
380 .getDeviceMac(deviceId)));
381 fob.permit().fromApp(srManager.appId);
382 log.debug("populateTableTMac: Installing filtering objective for router mac");
383 srManager.flowObjectiveService.
385 fob.add(new SRObjectiveContext(deviceId,
386 SRObjectiveContext.ObjectiveType.FILTER)));
389 private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
391 Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
392 for (DeviceId destId: destIds) {
393 for (Link link : links) {
394 if (link.dst().deviceId().equals(destId)) {
395 return link.src().port();
396 } else if (link.src().deviceId().equals(destId)) {
397 return link.dst().port();
405 private static class SRObjectiveContext implements ObjectiveContext {
410 final DeviceId deviceId;
411 final ObjectiveType type;
413 SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
414 this.deviceId = deviceId;
418 public void onSuccess(Objective objective) {
419 log.debug("{} objective operation successful in device {}",
420 type.name(), deviceId);
424 public void onError(Objective objective, ObjectiveError error) {
425 log.warn("{} objective {} operation failed with error: {} in device {}",
426 type.name(), objective, error, deviceId);