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.Port;
29 import org.onosproject.net.PortNumber;
30 import org.onosproject.net.flow.DefaultTrafficSelector;
31 import org.onosproject.net.flow.DefaultTrafficTreatment;
32 import org.onosproject.net.flow.TrafficSelector;
33 import org.onosproject.net.flow.TrafficTreatment;
34 import org.onosproject.net.flow.criteria.Criteria;
35 import org.onosproject.net.flowobjective.DefaultFilteringObjective;
36 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
37 import org.onosproject.net.flowobjective.FilteringObjective;
38 import org.onosproject.net.flowobjective.ForwardingObjective;
39 import org.onosproject.net.flowobjective.Objective;
40 import org.onosproject.net.flowobjective.ObjectiveError;
41 import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
42 import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
43 import org.onosproject.net.flowobjective.ObjectiveContext;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import java.util.ArrayList;
48 import java.util.HashSet;
49 import java.util.List;
51 import java.util.concurrent.atomic.AtomicLong;
53 import static com.google.common.base.Preconditions.checkNotNull;
55 public class RoutingRulePopulator {
57 private static final Logger log = LoggerFactory
58 .getLogger(RoutingRulePopulator.class);
60 private AtomicLong rulePopulationCounter;
61 private SegmentRoutingManager srManager;
62 private DeviceConfiguration config;
64 private static final int HIGHEST_PRIORITY = 0xffff;
65 private static final long OFPP_MAX = 0xffffff00L;
69 * Creates a RoutingRulePopulator object.
71 * @param srManager segment routing manager reference
73 public RoutingRulePopulator(SegmentRoutingManager srManager) {
74 this.srManager = srManager;
75 this.config = checkNotNull(srManager.deviceConfiguration);
76 this.rulePopulationCounter = new AtomicLong(0);
80 * Resets the population counter.
82 public void resetCounter() {
83 rulePopulationCounter.set(0);
87 * Returns the number of rules populated.
89 * @return number of rules
91 public long getCounter() {
92 return rulePopulationCounter.get();
96 * Populates IP flow rules for specific hosts directly connected to the
99 * @param deviceId switch ID to set the rules
100 * @param hostIp host IP address
101 * @param hostMac host MAC address
102 * @param outPort port where the host is connected
104 public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
105 MacAddress hostMac, PortNumber outPort) {
106 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
107 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
109 sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, IpPrefix.MAX_INET_MASK_LENGTH));
110 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
114 .setEthSrc(config.getDeviceMac(deviceId))
117 TrafficTreatment treatment = tbuilder.build();
118 TrafficSelector selector = sbuilder.build();
120 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
121 .builder().fromApp(srManager.appId).makePermanent()
122 .withSelector(selector).withTreatment(treatment)
123 .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
125 log.debug("Installing IPv4 forwarding objective "
126 + "for host {} in switch {}", hostIp, deviceId);
127 srManager.flowObjectiveService.
130 add(new SRObjectiveContext(deviceId,
131 SRObjectiveContext.ObjectiveType.FORWARDING)));
132 rulePopulationCounter.incrementAndGet();
136 * Populates IP flow rules for the subnets of the destination router.
138 * @param deviceId switch ID to set the rules
139 * @param subnets subnet information
140 * @param destSw destination switch ID
141 * @param nextHops next hop switch ID list
142 * @return true if all rules are set successfully, false otherwise
144 public boolean populateIpRuleForSubnet(DeviceId deviceId,
145 Set<Ip4Prefix> subnets,
147 Set<DeviceId> nextHops) {
149 for (IpPrefix subnet : subnets) {
150 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
159 * Populates IP flow rules for the router IP address.
161 * @param deviceId target device ID to set the rules
162 * @param ipPrefix the IP address of the destination router
163 * @param destSw device ID of the destination router
164 * @param nextHops next hop switch ID list
165 * @return true if all rules are set successfully, false otherwise
167 public boolean populateIpRuleForRouter(DeviceId deviceId,
168 IpPrefix ipPrefix, DeviceId destSw,
169 Set<DeviceId> nextHops) {
171 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
172 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
174 sbuilder.matchIPDst(ipPrefix);
175 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
177 NeighborSet ns = null;
179 // If the next hop is the same as the final destination, then MPLS label
181 if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
182 tbuilder.deferred().decNwTtl();
183 ns = new NeighborSet(nextHops);
185 tbuilder.deferred().copyTtlOut();
186 ns = new NeighborSet(nextHops, config.getSegmentId(destSw));
189 TrafficTreatment treatment = tbuilder.build();
190 TrafficSelector selector = sbuilder.build();
192 if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
193 log.warn("No next objective in {} for ns: {}", deviceId, ns);
197 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
199 .fromApp(srManager.appId)
201 .nextStep(srManager.getNextObjectiveId(deviceId, ns))
202 .withTreatment(treatment)
203 .withSelector(selector)
205 .withFlag(ForwardingObjective.Flag.SPECIFIC);
206 log.debug("Installing IPv4 forwarding objective "
207 + "for router IP/subnet {} in switch {}",
210 srManager.flowObjectiveService.
213 add(new SRObjectiveContext(deviceId,
214 SRObjectiveContext.ObjectiveType.FORWARDING)));
215 rulePopulationCounter.incrementAndGet();
221 * Populates MPLS flow rules to all routers.
223 * @param deviceId target device ID of the switch to set the rules
224 * @param destSwId destination switch device ID
225 * @param nextHops next hops switch ID list
226 * @return true if all rules are set successfully, false otherwise
228 public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId,
229 Set<DeviceId> nextHops) {
231 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
232 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
234 // TODO Handle the case of Bos == false
235 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getSegmentId(destSwId)));
236 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
238 // If the next hop is the destination router, do PHP
239 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
240 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
241 + "label {} in switch {} with PHP",
242 config.getSegmentId(destSwId),
245 ForwardingObjective.Builder fwdObjBosBuilder =
246 getMplsForwardingObjective(deviceId,
251 // TODO: Check with Sangho on why we need this
252 ForwardingObjective.Builder fwdObjNoBosBuilder =
253 getMplsForwardingObjective(deviceId,
258 if (fwdObjBosBuilder != null) {
259 fwdObjBuilders.add(fwdObjBosBuilder);
261 log.warn("Failed to set MPLS rules.");
265 log.debug("Installing MPLS forwarding objective for "
266 + "label {} in switch {} without PHP",
267 config.getSegmentId(destSwId),
270 ForwardingObjective.Builder fwdObjBosBuilder =
271 getMplsForwardingObjective(deviceId,
276 // TODO: Check with Sangho on why we need this
277 ForwardingObjective.Builder fwdObjNoBosBuilder =
278 getMplsForwardingObjective(deviceId,
283 if (fwdObjBosBuilder != null) {
284 fwdObjBuilders.add(fwdObjBosBuilder);
286 log.warn("Failed to set MPLS rules.");
291 TrafficSelector selector = sbuilder.build();
292 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
293 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
294 .makePermanent()).withSelector(selector)
296 .withFlag(ForwardingObjective.Flag.SPECIFIC);
297 srManager.flowObjectiveService.
300 add(new SRObjectiveContext(deviceId,
301 SRObjectiveContext.ObjectiveType.FORWARDING)));
302 rulePopulationCounter.incrementAndGet();
308 private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId,
310 Set<DeviceId> nextHops,
314 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
315 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
317 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
320 log.debug("getMplsForwardingObjective: php required");
321 tbuilder.deferred().copyTtlIn();
323 tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl();
325 tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
328 log.debug("getMplsForwardingObjective: php not required");
329 tbuilder.deferred().decMplsTtl();
332 if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) {
333 PortNumber port = selectOnePort(deviceId, nextHops);
334 DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
336 log.warn("No link from {} to {}", deviceId, nextHops);
340 .setEthSrc(config.getDeviceMac(deviceId))
341 .setEthDst(config.getDeviceMac(nextHop))
343 fwdBuilder.withTreatment(tbuilder.build());
345 NeighborSet ns = new NeighborSet(nextHops);
346 fwdBuilder.withTreatment(tbuilder.build());
347 fwdBuilder.nextStep(srManager
348 .getNextObjectiveId(deviceId, ns));
354 private boolean isECMPSupportedInTransitRouter() {
356 // TODO: remove this function when objectives subsystem is supported.
361 * Creates a filtering objective to permit all untagged packets with a
362 * dstMac corresponding to the router's MAC address. For those pipelines
363 * that need to internally assign vlans to untagged packets, this method
364 * provides per-subnet vlan-ids as metadata.
366 * Note that the vlan assignment is only done by the master-instance for a switch.
367 * However we send the filtering objective from slave-instances as well, so
368 * that drivers can obtain other information (like Router MAC and IP).
370 * @param deviceId the switch dpid for the router
372 public void populateRouterMacVlanFilters(DeviceId deviceId) {
373 log.debug("Installing per-port filtering objective for untagged "
374 + "packets in device {}", deviceId);
375 for (Port port : srManager.deviceService.getPorts(deviceId)) {
376 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
377 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
378 VlanId assignedVlan = (portSubnet == null)
379 ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
380 : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
381 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
382 fob.withKey(Criteria.matchInPort(port.number()))
383 .addCondition(Criteria.matchEthDst(config.getDeviceMac(deviceId)))
384 .addCondition(Criteria.matchVlanId(VlanId.NONE));
385 // vlan assignment is valid only if this instance is master
386 if (srManager.mastershipService.isLocalMaster(deviceId)) {
387 TrafficTreatment tt = DefaultTrafficTreatment.builder()
388 .pushVlan().setVlanId(assignedVlan).build();
391 fob.permit().fromApp(srManager.appId);
392 srManager.flowObjectiveService.
393 filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
394 SRObjectiveContext.ObjectiveType.FILTER)));
400 * Creates a forwarding objective to punt all IP packets, destined to the
401 * router's port IP addresses, to the controller. Note that the input
402 * port should not be matched on, as these packets can come from any input.
403 * Furthermore, these are applied only by the master instance.
405 * @param deviceId the switch dpid for the router
407 public void populateRouterIpPunts(DeviceId deviceId) {
408 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
409 log.debug("Not installing port-IP punts - not the master for dev:{} ",
413 ForwardingObjective.Builder puntIp = DefaultForwardingObjective.builder();
414 Set<Ip4Address> allIps = new HashSet<Ip4Address>(config.getPortIPs(deviceId));
415 allIps.add(config.getRouterIp(deviceId));
416 for (Ip4Address ipaddr : allIps) {
417 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
418 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
419 selector.matchEthType(Ethernet.TYPE_IPV4);
420 selector.matchIPDst(IpPrefix.valueOf(ipaddr,
421 IpPrefix.MAX_INET_MASK_LENGTH));
422 treatment.setOutput(PortNumber.CONTROLLER);
423 puntIp.withSelector(selector.build());
424 puntIp.withTreatment(treatment.build());
425 puntIp.withFlag(Flag.VERSATILE)
426 .withPriority(HIGHEST_PRIORITY)
428 .fromApp(srManager.appId);
429 log.debug("Installing forwarding objective to punt port IP addresses");
430 srManager.flowObjectiveService.
432 puntIp.add(new SRObjectiveContext(deviceId,
433 SRObjectiveContext.ObjectiveType.FORWARDING)));
437 private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
439 Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
440 for (DeviceId destId: destIds) {
441 for (Link link : links) {
442 if (link.dst().deviceId().equals(destId)) {
443 return link.src().port();
444 } else if (link.src().deviceId().equals(destId)) {
445 return link.dst().port();
453 private static class SRObjectiveContext implements ObjectiveContext {
458 final DeviceId deviceId;
459 final ObjectiveType type;
461 SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
462 this.deviceId = deviceId;
466 public void onSuccess(Objective objective) {
467 log.debug("{} objective operation successful in device {}",
468 type.name(), deviceId);
472 public void onError(Objective objective, ObjectiveError error) {
473 log.warn("{} objective {} operation failed with error: {} in device {}",
474 type.name(), objective, error, deviceId);