7641571d9b76a513ff9304aa72f0ee5490d2212f
[onosfw.git] /
1 /*
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  */
16 package org.onosproject.segmentrouting;
17
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;
44
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Set;
48 import java.util.concurrent.atomic.AtomicLong;
49
50 import static com.google.common.base.Preconditions.checkNotNull;
51
52 public class RoutingRulePopulator {
53
54     private static final Logger log = LoggerFactory
55             .getLogger(RoutingRulePopulator.class);
56
57     private AtomicLong rulePopulationCounter;
58     private SegmentRoutingManager srManager;
59     private DeviceConfiguration config;
60     /**
61      * Creates a RoutingRulePopulator object.
62      *
63      * @param srManager segment routing manager reference
64      */
65     public RoutingRulePopulator(SegmentRoutingManager srManager) {
66         this.srManager = srManager;
67         this.config = checkNotNull(srManager.deviceConfiguration);
68         this.rulePopulationCounter = new AtomicLong(0);
69     }
70
71     /**
72      * Resets the population counter.
73      */
74     public void resetCounter() {
75         rulePopulationCounter.set(0);
76     }
77
78     /**
79      * Returns the number of rules populated.
80      *
81      * @return number of rules
82      */
83     public long getCounter() {
84         return rulePopulationCounter.get();
85     }
86
87     /**
88      * Populates IP flow rules for specific hosts directly connected to the
89      * switch.
90      *
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
95      */
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();
100
101         sbuilder.matchIPDst(IpPrefix.valueOf(hostIp, 32));
102         sbuilder.matchEthType(Ethernet.TYPE_IPV4);
103
104         tbuilder.deferred()
105                 .setEthDst(hostMac)
106                 .setEthSrc(config.getDeviceMac(deviceId))
107                 .setOutput(outPort);
108
109         TrafficTreatment treatment = tbuilder.build();
110         TrafficSelector selector = sbuilder.build();
111
112         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
113                 .builder().fromApp(srManager.appId).makePermanent()
114                 .withSelector(selector).withTreatment(treatment)
115                 .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
116
117         log.debug("Installing IPv4 forwarding objective "
118                 + "for host {} in switch {}", hostIp, deviceId);
119         srManager.flowObjectiveService.
120             forward(deviceId,
121                     fwdBuilder.
122                     add(new SRObjectiveContext(deviceId,
123                                            SRObjectiveContext.ObjectiveType.FORWARDING)));
124         rulePopulationCounter.incrementAndGet();
125     }
126
127     /**
128      * Populates IP flow rules for the subnets of the destination router.
129      *
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
135      */
136     public boolean populateIpRuleForSubnet(DeviceId deviceId,
137                                            List<Ip4Prefix> subnets,
138                                            DeviceId destSw,
139                                            Set<DeviceId> nextHops) {
140
141         for (IpPrefix subnet : subnets) {
142             if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
143                 return false;
144             }
145         }
146
147         return true;
148     }
149
150     /**
151      * Populates IP flow rules for the router IP address.
152      *
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
158      */
159     public boolean populateIpRuleForRouter(DeviceId deviceId,
160                                            IpPrefix ipPrefix, DeviceId destSw,
161                                            Set<DeviceId> nextHops) {
162
163         TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
164         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
165
166         sbuilder.matchIPDst(ipPrefix);
167         sbuilder.matchEthType(Ethernet.TYPE_IPV4);
168
169         NeighborSet ns = null;
170
171         // If the next hop is the same as the final destination, then MPLS label
172         // is not set.
173         if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
174             tbuilder.deferred().decNwTtl();
175             ns = new NeighborSet(nextHops);
176         } else {
177             tbuilder.deferred().copyTtlOut();
178             ns = new NeighborSet(nextHops, config.getSegmentId(destSw));
179         }
180
181         TrafficTreatment treatment = tbuilder.build();
182         TrafficSelector selector = sbuilder.build();
183
184         if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
185             log.warn("No next objective in {} for ns: {}", deviceId, ns);
186             return false;
187         }
188
189         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
190                 .builder()
191                 .fromApp(srManager.appId)
192                 .makePermanent()
193                 .nextStep(srManager.getNextObjectiveId(deviceId, ns))
194                 .withTreatment(treatment)
195                 .withSelector(selector)
196                 .withPriority(100)
197                 .withFlag(ForwardingObjective.Flag.SPECIFIC);
198         log.debug("Installing IPv4 forwarding objective "
199                         + "for router IP/subnet {} in switch {}",
200                 ipPrefix,
201                 deviceId);
202         srManager.flowObjectiveService.
203             forward(deviceId,
204                     fwdBuilder.
205                     add(new SRObjectiveContext(deviceId,
206                                                SRObjectiveContext.ObjectiveType.FORWARDING)));
207         rulePopulationCounter.incrementAndGet();
208
209         return true;
210     }
211
212     /**
213      * Populates MPLS flow rules to all routers.
214      *
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
219      */
220     public boolean populateMplsRule(DeviceId deviceId, DeviceId destSwId,
221                                     Set<DeviceId> nextHops) {
222
223         TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
224         List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
225
226         // TODO Handle the case of Bos == false
227         sbuilder.matchMplsLabel(MplsLabel.mplsLabel(config.getSegmentId(destSwId)));
228         sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
229
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),
235                     deviceId);
236
237             ForwardingObjective.Builder fwdObjBosBuilder =
238                     getMplsForwardingObjective(deviceId,
239                                                destSwId,
240                                                nextHops,
241                                                true,
242                                                true);
243             // TODO: Check with Sangho on why we need this
244             ForwardingObjective.Builder fwdObjNoBosBuilder =
245                     getMplsForwardingObjective(deviceId,
246                                                destSwId,
247                                                nextHops,
248                                                true,
249                                                false);
250             if (fwdObjBosBuilder != null) {
251                 fwdObjBuilders.add(fwdObjBosBuilder);
252             } else {
253                 log.warn("Failed to set MPLS rules.");
254                 return false;
255             }
256         } else {
257             log.debug("Installing MPLS forwarding objective for "
258                     + "label {} in switch {} without PHP",
259                     config.getSegmentId(destSwId),
260                     deviceId);
261
262             ForwardingObjective.Builder fwdObjBosBuilder =
263                     getMplsForwardingObjective(deviceId,
264                                                destSwId,
265                                                nextHops,
266                                                false,
267                                                true);
268             // TODO: Check with Sangho on why we need this
269             ForwardingObjective.Builder fwdObjNoBosBuilder =
270                     getMplsForwardingObjective(deviceId,
271                                                destSwId,
272                                                nextHops,
273                                                false,
274                                                false);
275             if (fwdObjBosBuilder != null) {
276                 fwdObjBuilders.add(fwdObjBosBuilder);
277             } else {
278                 log.warn("Failed to set MPLS rules.");
279                 return false;
280             }
281         }
282
283         TrafficSelector selector = sbuilder.build();
284         for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
285             ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
286                     .makePermanent()).withSelector(selector)
287                     .withPriority(100))
288                     .withFlag(ForwardingObjective.Flag.SPECIFIC);
289             srManager.flowObjectiveService.
290                 forward(deviceId,
291                         fwdObjBuilder.
292                         add(new SRObjectiveContext(deviceId,
293                                                    SRObjectiveContext.ObjectiveType.FORWARDING)));
294             rulePopulationCounter.incrementAndGet();
295         }
296
297         return true;
298     }
299
300     private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId,
301                                                                    DeviceId destSw,
302                                                                    Set<DeviceId> nextHops,
303                                                                    boolean phpRequired,
304                                                                    boolean isBos) {
305
306         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
307                 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
308
309         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
310
311         if (phpRequired) {
312             log.debug("getMplsForwardingObjective: php required");
313             tbuilder.deferred().copyTtlIn();
314             if (isBos) {
315                 tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl();
316             } else {
317                 tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl();
318             }
319         } else {
320             log.debug("getMplsForwardingObjective: php not required");
321             tbuilder.deferred().decMplsTtl();
322         }
323
324         if (!isECMPSupportedInTransitRouter() && !config.isEdgeDevice(deviceId)) {
325             PortNumber port = selectOnePort(deviceId, nextHops);
326             DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
327             if (port == null) {
328                 log.warn("No link from {} to {}", deviceId, nextHops);
329                 return null;
330             }
331             tbuilder.deferred()
332                     .setEthSrc(config.getDeviceMac(deviceId))
333                     .setEthDst(config.getDeviceMac(nextHop))
334                     .setOutput(port);
335             fwdBuilder.withTreatment(tbuilder.build());
336         } else {
337             NeighborSet ns = new NeighborSet(nextHops);
338             fwdBuilder.withTreatment(tbuilder.build());
339             fwdBuilder.nextStep(srManager
340                     .getNextObjectiveId(deviceId, ns));
341         }
342
343         return fwdBuilder;
344     }
345
346     private boolean isECMPSupportedInTransitRouter() {
347
348         // TODO: remove this function when objectives subsystem is supported.
349         return false;
350     }
351
352     /**
353      * Populates VLAN flows rules. All packets are forwarded to TMAC table.
354      *
355      * @param deviceId switch ID to set the rules
356      */
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.
364             filter(deviceId,
365                    fob.add(new SRObjectiveContext(deviceId,
366                                                   SRObjectiveContext.ObjectiveType.FILTER)));
367     }
368
369     /**
370      * Populates TMAC table rules. IP packets are forwarded to IP table. MPLS
371      * packets are forwarded to MPLS table.
372      *
373      * @param deviceId switch ID to set the rules
374      */
375     public void populateTableTMac(DeviceId deviceId) {
376
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.
384             filter(deviceId,
385                    fob.add(new SRObjectiveContext(deviceId,
386                                                   SRObjectiveContext.ObjectiveType.FILTER)));
387     }
388
389     private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
390
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();
398                 }
399             }
400         }
401
402         return null;
403     }
404
405     private static class SRObjectiveContext implements ObjectiveContext {
406         enum ObjectiveType {
407             FILTER,
408             FORWARDING
409         }
410         final DeviceId deviceId;
411         final ObjectiveType type;
412
413         SRObjectiveContext(DeviceId deviceId, ObjectiveType type) {
414             this.deviceId = deviceId;
415             this.type = type;
416         }
417         @Override
418         public void onSuccess(Objective objective) {
419             log.debug("{} objective operation successful in device {}",
420                       type.name(), deviceId);
421         }
422
423         @Override
424         public void onError(Objective objective, ObjectiveError error) {
425             log.warn("{} objective {} operation failed with error: {} in device {}",
426                      type.name(), objective, error, deviceId);
427         }
428     }
429
430 }