blob: e12fbccbed28f0e94f2f01aed99854b301936b1b [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
sanghob35a6192015-04-01 13:05:26 -07003 *
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.segmentrouting;
17
Saurav Das8a0732e2015-11-20 15:27:53 -080018import org.onlab.packet.EthType;
sanghob35a6192015-04-01 13:05:26 -070019import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070021import org.onlab.packet.Ip4Prefix;
sanghob35a6192015-04-01 13:05:26 -070022import org.onlab.packet.IpPrefix;
23import org.onlab.packet.MacAddress;
sanghob35a6192015-04-01 13:05:26 -070024import org.onlab.packet.MplsLabel;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070025import org.onlab.packet.VlanId;
Charles Chane849c192016-01-11 18:28:54 -080026import org.onosproject.net.ConnectPoint;
Charles Chand2990362016-04-18 13:44:03 -070027import org.onosproject.net.flowobjective.DefaultObjectiveContext;
28import org.onosproject.net.flowobjective.ObjectiveContext;
Charles Chan0b4e6182015-11-03 10:42:14 -080029import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
30import org.onosproject.segmentrouting.config.DeviceConfiguration;
Charles Chan6ea94fc2016-05-10 17:29:47 -070031import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070032import org.onosproject.segmentrouting.grouphandler.NeighborSet;
sanghob35a6192015-04-01 13:05:26 -070033import org.onosproject.net.DeviceId;
Saurav Das0e99e2b2015-10-28 12:39:42 -070034import org.onosproject.net.Port;
sanghob35a6192015-04-01 13:05:26 -070035import org.onosproject.net.PortNumber;
sanghob35a6192015-04-01 13:05:26 -070036import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
sanghob35a6192015-04-01 13:05:26 -070038import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070040import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.net.flowobjective.DefaultFilteringObjective;
42import org.onosproject.net.flowobjective.DefaultForwardingObjective;
43import org.onosproject.net.flowobjective.FilteringObjective;
44import org.onosproject.net.flowobjective.ForwardingObjective;
45import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
Saurav Das822c4e22015-10-23 10:51:11 -070046import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
sanghob35a6192015-04-01 13:05:26 -070047import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
50import java.util.ArrayList;
Saurav Das837e0bb2015-10-30 17:45:38 -070051import java.util.HashSet;
sanghob35a6192015-04-01 13:05:26 -070052import java.util.List;
53import java.util.Set;
sangho20eff1d2015-04-13 15:15:58 -070054import java.util.concurrent.atomic.AtomicLong;
sanghob35a6192015-04-01 13:05:26 -070055
56import static com.google.common.base.Preconditions.checkNotNull;
57
Charles Chane849c192016-01-11 18:28:54 -080058/**
59 * Populator of segment routing flow rules.
60 */
sanghob35a6192015-04-01 13:05:26 -070061public class RoutingRulePopulator {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070062 private static final Logger log = LoggerFactory
63 .getLogger(RoutingRulePopulator.class);
sanghob35a6192015-04-01 13:05:26 -070064
sangho20eff1d2015-04-13 15:15:58 -070065 private AtomicLong rulePopulationCounter;
sangho666cd6d2015-04-14 16:27:13 -070066 private SegmentRoutingManager srManager;
67 private DeviceConfiguration config;
Saurav Das822c4e22015-10-23 10:51:11 -070068
sanghob35a6192015-04-01 13:05:26 -070069 /**
70 * Creates a RoutingRulePopulator object.
71 *
Thomas Vachuskae10f56b2015-04-15 18:20:08 -070072 * @param srManager segment routing manager reference
sanghob35a6192015-04-01 13:05:26 -070073 */
74 public RoutingRulePopulator(SegmentRoutingManager srManager) {
75 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070076 this.config = checkNotNull(srManager.deviceConfiguration);
sangho20eff1d2015-04-13 15:15:58 -070077 this.rulePopulationCounter = new AtomicLong(0);
78 }
79
80 /**
81 * Resets the population counter.
82 */
83 public void resetCounter() {
84 rulePopulationCounter.set(0);
85 }
86
87 /**
88 * Returns the number of rules populated.
Thomas Vachuska266b4432015-04-30 18:13:25 -070089 *
90 * @return number of rules
sangho20eff1d2015-04-13 15:15:58 -070091 */
92 public long getCounter() {
93 return rulePopulationCounter.get();
sanghob35a6192015-04-01 13:05:26 -070094 }
95
96 /**
Charles Chan1cdecff2016-10-27 14:19:48 -070097 * Populates IP rules for a route that has direct connection to the
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070098 * switch.
sanghob35a6192015-04-01 13:05:26 -070099 *
Charles Chan1cdecff2016-10-27 14:19:48 -0700100 * @param deviceId device ID of the device that next hop attaches to
101 * @param prefix IP prefix of the route
102 * @param hostMac MAC address of the next hop
103 * @param outPort port where the next hop attaches to
sanghob35a6192015-04-01 13:05:26 -0700104 */
Charles Chan1cdecff2016-10-27 14:19:48 -0700105 public void populateRoute(DeviceId deviceId, IpPrefix prefix,
sanghob35a6192015-04-01 13:05:26 -0700106 MacAddress hostMac, PortNumber outPort) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700107 log.debug("Populate IP table entry for route {} at {}:{}",
108 prefix, deviceId, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800109 ForwardingObjective.Builder fwdBuilder;
Charles Chan0b4e6182015-11-03 10:42:14 -0800110 try {
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 fwdBuilder = getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700112 deviceId, prefix, hostMac, outPort);
Charles Chan0b4e6182015-11-03 10:42:14 -0800113 } catch (DeviceConfigNotFoundException e) {
114 log.warn(e.getMessage() + " Aborting populateIpRuleForHost.");
115 return;
116 }
Saurav Das59232cf2016-04-27 18:35:50 -0700117 if (fwdBuilder == null) {
118 log.warn("Aborting host routing table entries due "
Charles Chan1cdecff2016-10-27 14:19:48 -0700119 + "to error for dev:{} route:{}", deviceId, prefix);
Saurav Das59232cf2016-04-27 18:35:50 -0700120 return;
121 }
Charles Chand2990362016-04-18 13:44:03 -0700122 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan1cdecff2016-10-27 14:19:48 -0700123 (objective) -> log.debug("IP rule for route {} populated", prefix),
Charles Chand2990362016-04-18 13:44:03 -0700124 (objective, error) ->
Charles Chan1cdecff2016-10-27 14:19:48 -0700125 log.warn("Failed to populate IP rule for route {}: {}", prefix, error));
Charles Chand2990362016-04-18 13:44:03 -0700126 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800127 rulePopulationCounter.incrementAndGet();
128 }
129
Charles Chane849c192016-01-11 18:28:54 -0800130 /**
Charles Chan1cdecff2016-10-27 14:19:48 -0700131 * Removes IP rules for a route when the next hop is gone.
Charles Chane849c192016-01-11 18:28:54 -0800132 *
Charles Chan1cdecff2016-10-27 14:19:48 -0700133 * @param deviceId device ID of the device that next hop attaches to
134 * @param prefix IP prefix of the route
135 * @param hostMac MAC address of the next hop
136 * @param outPort port that next hop attaches to
Charles Chane849c192016-01-11 18:28:54 -0800137 */
Charles Chan1cdecff2016-10-27 14:19:48 -0700138 public void revokeRoute(DeviceId deviceId, IpPrefix prefix,
Charles Chan68aa62d2015-11-09 16:37:23 -0800139 MacAddress hostMac, PortNumber outPort) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700140 log.debug("Revoke IP table entry for route {} at {}:{}",
141 prefix, deviceId, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800142 ForwardingObjective.Builder fwdBuilder;
143 try {
144 fwdBuilder = getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700145 deviceId, prefix, hostMac, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800146 } catch (DeviceConfigNotFoundException e) {
147 log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
148 return;
149 }
Charles Chan458b8262016-11-30 11:55:05 -0800150 if (fwdBuilder == null) {
151 log.warn("Aborting host routing table entries due "
152 + "to error for dev:{} route:{}", deviceId, prefix);
153 return;
154 }
Charles Chand2990362016-04-18 13:44:03 -0700155 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan1cdecff2016-10-27 14:19:48 -0700156 (objective) -> log.debug("IP rule for route {} revoked", prefix),
Charles Chand2990362016-04-18 13:44:03 -0700157 (objective, error) ->
Charles Chan1cdecff2016-10-27 14:19:48 -0700158 log.warn("Failed to revoke IP rule for route {}: {}", prefix, error));
Charles Chand2990362016-04-18 13:44:03 -0700159 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800160 }
161
Charles Chan1cdecff2016-10-27 14:19:48 -0700162 /**
163 * Returns a forwarding objective that points packets destined to a
164 * given prefix to given port on given device with given destination MAC.
165 *
166 * @param deviceId device ID
167 * @param prefix prefix that need to be routed
168 * @param hostMac MAC address of the nexthop
169 * @param outPort port where the nexthop attaches to
170 * @return forwarding objective builder
171 * @throws DeviceConfigNotFoundException if given device is not configured
172 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800173 private ForwardingObjective.Builder getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700174 DeviceId deviceId, IpPrefix prefix,
Charles Chan68aa62d2015-11-09 16:37:23 -0800175 MacAddress hostMac, PortNumber outPort)
176 throws DeviceConfigNotFoundException {
177 MacAddress deviceMac;
178 deviceMac = config.getDeviceMac(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800179
sanghob35a6192015-04-01 13:05:26 -0700180 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sanghob35a6192015-04-01 13:05:26 -0700181 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan1cdecff2016-10-27 14:19:48 -0700182 sbuilder.matchIPDst(prefix);
Saurav Das4ce45962015-11-24 23:21:05 -0800183 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700184
Charles Chan1cdecff2016-10-27 14:19:48 -0700185 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sangho1e575652015-05-14 00:39:53 -0700186 tbuilder.deferred()
187 .setEthDst(hostMac)
Charles Chan0b4e6182015-11-03 10:42:14 -0800188 .setEthSrc(deviceMac)
sanghob35a6192015-04-01 13:05:26 -0700189 .setOutput(outPort);
sanghob35a6192015-04-01 13:05:26 -0700190 TrafficTreatment treatment = tbuilder.build();
Saurav Das4ce45962015-11-24 23:21:05 -0800191
192 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
193 // for switch pipelines that need it, provide outgoing vlan as metadata
194 VlanId outvlan = null;
195 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outPort);
196 if (subnet == null) {
197 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
198 } else {
199 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
200 }
201 TrafficSelector meta = DefaultTrafficSelector.builder()
202 .matchVlanId(outvlan).build();
203 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
204 treatment, meta);
Saurav Das59232cf2016-04-27 18:35:50 -0700205 if (portNextObjId == -1) {
206 // warning log will come from getPortNextObjective method
207 return null;
208 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800209 return DefaultForwardingObjective.builder()
Saurav Das4ce45962015-11-24 23:21:05 -0800210 .withSelector(selector)
211 .nextStep(portNextObjId)
Charles Chan68aa62d2015-11-09 16:37:23 -0800212 .fromApp(srManager.appId).makePermanent()
Charles Chan1cdecff2016-10-27 14:19:48 -0700213 .withPriority(getPriorityFromPrefix(prefix))
Charles Chan5270ed02016-01-30 23:22:37 -0800214 .withFlag(ForwardingObjective.Flag.SPECIFIC);
sanghob35a6192015-04-01 13:05:26 -0700215 }
216
217 /**
218 * Populates IP flow rules for the subnets of the destination router.
219 *
220 * @param deviceId switch ID to set the rules
Charles Chan93e71ba2016-04-29 14:38:22 -0700221 * @param subnets subnet being added
sanghob35a6192015-04-01 13:05:26 -0700222 * @param destSw destination switch ID
223 * @param nextHops next hop switch ID list
224 * @return true if all rules are set successfully, false otherwise
225 */
Charles Chan93e71ba2016-04-29 14:38:22 -0700226 public boolean populateIpRuleForSubnet(DeviceId deviceId, Set<Ip4Prefix> subnets,
227 DeviceId destSw, Set<DeviceId> nextHops) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700228 for (IpPrefix subnet : subnets) {
sanghob35a6192015-04-01 13:05:26 -0700229 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
230 return false;
231 }
232 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700233 return true;
234 }
sanghob35a6192015-04-01 13:05:26 -0700235
Charles Chan93e71ba2016-04-29 14:38:22 -0700236 /**
237 * Revokes IP flow rules for the subnets.
238 *
239 * @param subnets subnet being removed
240 * @return true if all rules are removed successfully, false otherwise
241 */
242 public boolean revokeIpRuleForSubnet(Set<Ip4Prefix> subnets) {
243 for (IpPrefix subnet : subnets) {
244 if (!revokeIpRuleForRouter(subnet)) {
245 return false;
246 }
247 }
sanghob35a6192015-04-01 13:05:26 -0700248 return true;
249 }
250
251 /**
Saurav Das25190812016-05-27 13:54:07 -0700252 * Populates IP flow rules for an IP prefix in the target device. The prefix
253 * is reachable via destination device.
sanghob35a6192015-04-01 13:05:26 -0700254 *
Saurav Dasa07f2032015-10-19 14:37:36 -0700255 * @param deviceId target device ID to set the rules
sanghob35a6192015-04-01 13:05:26 -0700256 * @param ipPrefix the IP address of the destination router
257 * @param destSw device ID of the destination router
258 * @param nextHops next hop switch ID list
259 * @return true if all rules are set successfully, false otherwise
260 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700261 public boolean populateIpRuleForRouter(DeviceId deviceId,
262 IpPrefix ipPrefix, DeviceId destSw,
263 Set<DeviceId> nextHops) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800264 int segmentId;
265 try {
266 segmentId = config.getSegmentId(destSw);
267 } catch (DeviceConfigNotFoundException e) {
268 log.warn(e.getMessage() + " Aborting populateIpRuleForRouter.");
269 return false;
270 }
sanghob35a6192015-04-01 13:05:26 -0700271
272 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
Charles Chan17d38f42016-02-05 13:33:54 -0800273 sbuilder.matchIPDst(ipPrefix);
sanghob35a6192015-04-01 13:05:26 -0700274 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800275 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700276
Charles Chan68aa62d2015-11-09 16:37:23 -0800277 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
278 NeighborSet ns;
279 TrafficTreatment treatment;
sanghob35a6192015-04-01 13:05:26 -0700280
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700281 // If the next hop is the same as the final destination, then MPLS label
282 // is not set.
sanghob35a6192015-04-01 13:05:26 -0700283 if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800284 tbuilder.immediate().decNwTtl();
sanghob35a6192015-04-01 13:05:26 -0700285 ns = new NeighborSet(nextHops);
Charles Chan68aa62d2015-11-09 16:37:23 -0800286 treatment = tbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700287 } else {
Charles Chan0b4e6182015-11-03 10:42:14 -0800288 ns = new NeighborSet(nextHops, segmentId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800289 treatment = null;
sanghob35a6192015-04-01 13:05:26 -0700290 }
291
Saurav Das8a0732e2015-11-20 15:27:53 -0800292 // setup metadata to pass to nextObjective - indicate the vlan on egress
293 // if needed by the switch pipeline. Since neighbor sets are always to
294 // other neighboring routers, there is no subnet assigned on those ports.
295 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
296 metabuilder.matchVlanId(
297 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
298
299 int nextId = srManager.getNextObjectiveId(deviceId, ns, metabuilder.build());
300 if (nextId <= 0) {
sangho834e4b02015-05-01 09:38:25 -0700301 log.warn("No next objective in {} for ns: {}", deviceId, ns);
302 return false;
303 }
304
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700305 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
306 .builder()
307 .fromApp(srManager.appId)
308 .makePermanent()
Saurav Das8a0732e2015-11-20 15:27:53 -0800309 .nextStep(nextId)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700310 .withSelector(selector)
Charles Chan5270ed02016-01-30 23:22:37 -0800311 .withPriority(getPriorityFromPrefix(ipPrefix))
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700312 .withFlag(ForwardingObjective.Flag.SPECIFIC);
Charles Chan68aa62d2015-11-09 16:37:23 -0800313 if (treatment != null) {
314 fwdBuilder.withTreatment(treatment);
315 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700316 log.debug("Installing IPv4 forwarding objective "
sangho834e4b02015-05-01 09:38:25 -0700317 + "for router IP/subnet {} in switch {}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700318 ipPrefix,
319 deviceId);
Charles Chand2990362016-04-18 13:44:03 -0700320 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das25190812016-05-27 13:54:07 -0700321 (objective) -> log.debug("IP rule for router {} populated in dev:{}",
322 ipPrefix, deviceId),
Charles Chand2990362016-04-18 13:44:03 -0700323 (objective, error) ->
Saurav Das25190812016-05-27 13:54:07 -0700324 log.warn("Failed to populate IP rule for router {}: {} in dev:{}",
325 ipPrefix, error, deviceId));
Charles Chand2990362016-04-18 13:44:03 -0700326 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
sangho20eff1d2015-04-13 15:15:58 -0700327 rulePopulationCounter.incrementAndGet();
sanghob35a6192015-04-01 13:05:26 -0700328
329 return true;
330 }
331
sanghob35a6192015-04-01 13:05:26 -0700332 /**
Charles Chan93e71ba2016-04-29 14:38:22 -0700333 * Revokes IP flow rules for the router IP address.
334 *
335 * @param ipPrefix the IP address of the destination router
336 * @return true if all rules are removed successfully, false otherwise
337 */
338 public boolean revokeIpRuleForRouter(IpPrefix ipPrefix) {
339 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
340 sbuilder.matchIPDst(ipPrefix);
341 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
342 TrafficSelector selector = sbuilder.build();
343 TrafficTreatment dummyTreatment = DefaultTrafficTreatment.builder().build();
344
345 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
346 .builder()
347 .fromApp(srManager.appId)
348 .makePermanent()
349 .withSelector(selector)
350 .withTreatment(dummyTreatment)
351 .withPriority(getPriorityFromPrefix(ipPrefix))
352 .withFlag(ForwardingObjective.Flag.SPECIFIC);
353
354 ObjectiveContext context = new DefaultObjectiveContext(
355 (objective) -> log.debug("IP rule for router {} revoked", ipPrefix),
356 (objective, error) ->
357 log.warn("Failed to revoke IP rule for router {}: {}", ipPrefix, error));
358
359 srManager.deviceService.getAvailableDevices().forEach(device -> {
360 srManager.flowObjectiveService.forward(device.id(), fwdBuilder.remove(context));
361 });
362
363 return true;
364 }
365
366 /**
Saurav Das25190812016-05-27 13:54:07 -0700367 * Populates MPLS flow rules in the target device to point towards the
368 * destination device.
sanghob35a6192015-04-01 13:05:26 -0700369 *
Saurav Das25190812016-05-27 13:54:07 -0700370 * @param targetSwId target device ID of the switch to set the rules
sanghob35a6192015-04-01 13:05:26 -0700371 * @param destSwId destination switch device ID
372 * @param nextHops next hops switch ID list
373 * @return true if all rules are set successfully, false otherwise
374 */
Saurav Das25190812016-05-27 13:54:07 -0700375 public boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700376 Set<DeviceId> nextHops) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800377 int segmentId;
378 try {
379 segmentId = config.getSegmentId(destSwId);
380 } catch (DeviceConfigNotFoundException e) {
381 log.warn(e.getMessage() + " Aborting populateMplsRule.");
382 return false;
383 }
sanghob35a6192015-04-01 13:05:26 -0700384
385 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700386 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700387
388 // TODO Handle the case of Bos == false
sanghob35a6192015-04-01 13:05:26 -0700389 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
Saurav Das8a0732e2015-11-20 15:27:53 -0800390 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
Charles Chan188ebf52015-12-23 00:15:11 -0800391 sbuilder.matchMplsBos(true);
Saurav Das8a0732e2015-11-20 15:27:53 -0800392 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700393
Saurav Das8a0732e2015-11-20 15:27:53 -0800394 // setup metadata to pass to nextObjective - indicate the vlan on egress
395 // if needed by the switch pipeline. Since mpls next-hops are always to
396 // other neighboring routers, there is no subnet assigned on those ports.
397 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
398 metabuilder.matchVlanId(
399 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
400
401 // If the next hop is the destination router for the segment, do pop
sanghob35a6192015-04-01 13:05:26 -0700402 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700403 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
Saurav Das25190812016-05-27 13:54:07 -0700404 + "label {} in switch {} with pop", segmentId, targetSwId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700405
Saurav Das8a0732e2015-11-20 15:27:53 -0800406 // bos pop case (php)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700407 ForwardingObjective.Builder fwdObjBosBuilder =
Saurav Das25190812016-05-27 13:54:07 -0700408 getMplsForwardingObjective(targetSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700409 nextHops,
410 true,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700411 true,
Saurav Das8a0732e2015-11-20 15:27:53 -0800412 metabuilder.build());
413 if (fwdObjBosBuilder == null) {
sanghob35a6192015-04-01 13:05:26 -0700414 return false;
415 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800416 fwdObjBuilders.add(fwdObjBosBuilder);
417
418 // XXX not-bos pop case, SR app multi-label not implemented yet
419 /*ForwardingObjective.Builder fwdObjNoBosBuilder =
420 getMplsForwardingObjective(deviceId,
421 nextHops,
422 true,
423 false);*/
424
sanghob35a6192015-04-01 13:05:26 -0700425 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800426 // next hop is not destination, SR CONTINUE case (swap with self)
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700427 log.debug("Installing MPLS forwarding objective for "
Saurav Das25190812016-05-27 13:54:07 -0700428 + "label {} in switch {} without pop", segmentId, targetSwId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700429
Saurav Das8a0732e2015-11-20 15:27:53 -0800430 // continue case with bos - this does get triggered in edge routers
431 // and in core routers - driver can handle depending on availability
432 // of MPLS ECMP or not
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700433 ForwardingObjective.Builder fwdObjBosBuilder =
Saurav Das25190812016-05-27 13:54:07 -0700434 getMplsForwardingObjective(targetSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700435 nextHops,
436 false,
Saurav Das8a0732e2015-11-20 15:27:53 -0800437 true,
438 metabuilder.build());
439 if (fwdObjBosBuilder == null) {
sanghob35a6192015-04-01 13:05:26 -0700440 return false;
441 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800442 fwdObjBuilders.add(fwdObjBosBuilder);
443
444 // XXX continue case with not-bos - SR app multi label not implemented yet
445 // also requires MPLS ECMP
446 /*ForwardingObjective.Builder fwdObjNoBosBuilder =
447 getMplsForwardingObjective(deviceId,
448 nextHops,
449 false,
450 false); */
451
sanghob35a6192015-04-01 13:05:26 -0700452 }
Saurav Das25190812016-05-27 13:54:07 -0700453 // XXX when other cases above are implemented check for validity of
454 // debug messages below
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700455 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
456 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
457 .makePermanent()).withSelector(selector)
Charles Chan5270ed02016-01-30 23:22:37 -0800458 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700459 .withFlag(ForwardingObjective.Flag.SPECIFIC);
Charles Chand2990362016-04-18 13:44:03 -0700460 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das25190812016-05-27 13:54:07 -0700461 (objective) -> log.debug("MPLS rule {} for SID {} populated in dev:{} ",
462 objective.id(), segmentId, targetSwId),
Charles Chand2990362016-04-18 13:44:03 -0700463 (objective, error) ->
Saurav Das25190812016-05-27 13:54:07 -0700464 log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
465 objective.id(), segmentId, error, targetSwId));
466 ForwardingObjective fob = fwdObjBuilder.add(context);
467 log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
468 fob.id(), segmentId, fob.nextId(), targetSwId);
469 srManager.flowObjectiveService.forward(targetSwId, fob);
sangho20eff1d2015-04-13 15:15:58 -0700470 rulePopulationCounter.incrementAndGet();
sanghob35a6192015-04-01 13:05:26 -0700471 }
472
473 return true;
474 }
475
Saurav Das8a0732e2015-11-20 15:27:53 -0800476 private ForwardingObjective.Builder getMplsForwardingObjective(
477 DeviceId deviceId,
478 Set<DeviceId> nextHops,
479 boolean phpRequired,
480 boolean isBos,
481 TrafficSelector meta) {
482
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700483 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
484 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
sanghob35a6192015-04-01 13:05:26 -0700485
486 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
487
488 if (phpRequired) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800489 // php case - pop should always be flow-action
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700490 log.debug("getMplsForwardingObjective: php required");
sangho1e575652015-05-14 00:39:53 -0700491 tbuilder.deferred().copyTtlIn();
sanghob35a6192015-04-01 13:05:26 -0700492 if (isBos) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800493 tbuilder.deferred().popMpls(EthType.EtherType.IPV4.ethType())
494 .decNwTtl();
sanghob35a6192015-04-01 13:05:26 -0700495 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800496 tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
497 .decMplsTtl();
sanghob35a6192015-04-01 13:05:26 -0700498 }
499 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800500 // swap with self case - SR CONTINUE
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700501 log.debug("getMplsForwardingObjective: php not required");
sangho1e575652015-05-14 00:39:53 -0700502 tbuilder.deferred().decMplsTtl();
sanghob35a6192015-04-01 13:05:26 -0700503 }
504
Saurav Das8a0732e2015-11-20 15:27:53 -0800505 // All forwarding is via ECMP group, the metadata informs the driver
506 // that the next-Objective will be used by MPLS flows. In other words,
507 // MPLS ECMP is requested. It is up to the driver to decide if these
508 // packets will be hashed or not.
509 fwdBuilder.withTreatment(tbuilder.build());
510 NeighborSet ns = new NeighborSet(nextHops);
Saurav Das25190812016-05-27 13:54:07 -0700511 log.debug("Trying to get a nextObjId for mpls rule on device:{} to ns:{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800512 deviceId, ns);
513
514 int nextId = srManager.getNextObjectiveId(deviceId, ns, meta);
515 if (nextId <= 0) {
516 log.warn("No next objective in {} for ns: {}", deviceId, ns);
517 return null;
Saurav Das25190812016-05-27 13:54:07 -0700518 } else {
519 log.debug("nextObjId found:{} for mpls rule on device:{} to ns:{}",
520 nextId, deviceId, ns);
sanghob35a6192015-04-01 13:05:26 -0700521 }
522
Saurav Das8a0732e2015-11-20 15:27:53 -0800523 fwdBuilder.nextStep(nextId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700524 return fwdBuilder;
sanghob35a6192015-04-01 13:05:26 -0700525 }
526
527 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700528 * Creates a filtering objective to permit all untagged packets with a
Saurav Das0e99e2b2015-10-28 12:39:42 -0700529 * dstMac corresponding to the router's MAC address. For those pipelines
530 * that need to internally assign vlans to untagged packets, this method
531 * provides per-subnet vlan-ids as metadata.
Saurav Das837e0bb2015-10-30 17:45:38 -0700532 * <p>
533 * Note that the vlan assignment is only done by the master-instance for a switch.
534 * However we send the filtering objective from slave-instances as well, so
535 * that drivers can obtain other information (like Router MAC and IP).
sanghob35a6192015-04-01 13:05:26 -0700536 *
Saurav Das822c4e22015-10-23 10:51:11 -0700537 * @param deviceId the switch dpid for the router
Charles Chan93e71ba2016-04-29 14:38:22 -0700538 * @return true if operation succeeds
sanghob35a6192015-04-01 13:05:26 -0700539 */
Saurav Das59232cf2016-04-27 18:35:50 -0700540 public boolean populateRouterMacVlanFilters(DeviceId deviceId) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700541 log.debug("Installing per-port filtering objective for untagged "
542 + "packets in device {}", deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800543
544 MacAddress deviceMac;
545 try {
546 deviceMac = config.getDeviceMac(deviceId);
547 } catch (DeviceConfigNotFoundException e) {
548 log.warn(e.getMessage() + " Aborting populateRouterMacVlanFilters.");
Saurav Das59232cf2016-04-27 18:35:50 -0700549 return false;
Charles Chan0b4e6182015-11-03 10:42:14 -0800550 }
551
Saurav Das59232cf2016-04-27 18:35:50 -0700552 List<Port> devPorts = srManager.deviceService.getPorts(deviceId);
553 if (devPorts != null && devPorts.size() == 0) {
554 log.warn("Device {} ports not available. Unable to add MacVlan filters",
555 deviceId);
556 return false;
557 }
Saurav Das25190812016-05-27 13:54:07 -0700558 int disabledPorts = 0, suppressedPorts = 0, filteredPorts = 0;
Saurav Das59232cf2016-04-27 18:35:50 -0700559 for (Port port : devPorts) {
Charles Chand2990362016-04-18 13:44:03 -0700560 ConnectPoint connectPoint = new ConnectPoint(deviceId, port.number());
Charles Chanf2565a92016-02-10 20:46:58 -0800561 // TODO: Handles dynamic port events when we are ready for dynamic config
Charles Chan6ea94fc2016-05-10 17:29:47 -0700562 SegmentRoutingAppConfig appConfig = srManager.cfgService
563 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Saurav Das25190812016-05-27 13:54:07 -0700564 if (!port.isEnabled()) {
565 disabledPorts++;
566 continue;
567 }
568 if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
569 suppressedPorts++;
570 continue;
571 }
572 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
573 VlanId assignedVlan = (portSubnet == null)
574 ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
575 : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
Charles Chan0b4e6182015-11-03 10:42:14 -0800576
Saurav Das25190812016-05-27 13:54:07 -0700577 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
578 fob.withKey(Criteria.matchInPort(port.number()))
Charles Chan0b4e6182015-11-03 10:42:14 -0800579 .addCondition(Criteria.matchEthDst(deviceMac))
Charles Chane849c192016-01-11 18:28:54 -0800580 .addCondition(Criteria.matchVlanId(VlanId.NONE))
Charles Chan5270ed02016-01-30 23:22:37 -0800581 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
Saurav Das25190812016-05-27 13:54:07 -0700582 // vlan assignment is valid only if this instance is master
583 if (srManager.mastershipService.isLocalMaster(deviceId)) {
584 TrafficTreatment tt = DefaultTrafficTreatment.builder()
585 .pushVlan().setVlanId(assignedVlan).build();
586 fob.withMeta(tt);
Saurav Das0e99e2b2015-10-28 12:39:42 -0700587 }
Saurav Das25190812016-05-27 13:54:07 -0700588 fob.permit().fromApp(srManager.appId);
589 log.debug("Sending filtering objective for dev/port:{}/{}", deviceId, port);
590 filteredPorts++;
591 ObjectiveContext context = new DefaultObjectiveContext(
592 (objective) -> log.debug("Filter for {} populated", connectPoint),
593 (objective, error) ->
594 log.warn("Failed to populate filter for {}: {}", connectPoint, error));
595 srManager.flowObjectiveService.filter(deviceId, fob.add(context));
Saurav Das0e99e2b2015-10-28 12:39:42 -0700596 }
Saurav Das25190812016-05-27 13:54:07 -0700597 log.info("Filtering on dev:{}, disabledPorts:{}, suppressedPorts:{}, filteredPorts:{}",
598 deviceId, disabledPorts, suppressedPorts, filteredPorts);
599 // XXX With this check, there is a chance that not all the ports that
600 // should be filtered actually get filtered as long as one of them does.
601 // Note there is no PORT_UPDATED event that makes the port go from disabled
602 // to enabled state, because the ports comes enabled from the switch.
603 // Check ONOS core, where the port becoming available and being declared
604 // enabled is possibly not atomic.
605 return (filteredPorts > 0) ? true : false;
sanghob35a6192015-04-01 13:05:26 -0700606 }
607
608 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700609 * Creates a forwarding objective to punt all IP packets, destined to the
Saurav Das837e0bb2015-10-30 17:45:38 -0700610 * router's port IP addresses, to the controller. Note that the input
Saurav Das822c4e22015-10-23 10:51:11 -0700611 * port should not be matched on, as these packets can come from any input.
Saurav Das837e0bb2015-10-30 17:45:38 -0700612 * Furthermore, these are applied only by the master instance.
sanghob35a6192015-04-01 13:05:26 -0700613 *
Saurav Das822c4e22015-10-23 10:51:11 -0700614 * @param deviceId the switch dpid for the router
sanghob35a6192015-04-01 13:05:26 -0700615 */
Saurav Das822c4e22015-10-23 10:51:11 -0700616 public void populateRouterIpPunts(DeviceId deviceId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800617 Ip4Address routerIp;
618 try {
619 routerIp = config.getRouterIp(deviceId);
620 } catch (DeviceConfigNotFoundException e) {
621 log.warn(e.getMessage() + " Aborting populateRouterIpPunts.");
622 return;
623 }
624
Saurav Das837e0bb2015-10-30 17:45:38 -0700625 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
626 log.debug("Not installing port-IP punts - not the master for dev:{} ",
627 deviceId);
628 return;
629 }
Saurav Das822c4e22015-10-23 10:51:11 -0700630 ForwardingObjective.Builder puntIp = DefaultForwardingObjective.builder();
Charles Chan5270ed02016-01-30 23:22:37 -0800631 Set<Ip4Address> allIps = new HashSet<>(config.getPortIPs(deviceId));
Charles Chan0b4e6182015-11-03 10:42:14 -0800632 allIps.add(routerIp);
Saurav Das837e0bb2015-10-30 17:45:38 -0700633 for (Ip4Address ipaddr : allIps) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800634 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
635 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
636 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
637 sbuilder.matchIPDst(IpPrefix.valueOf(ipaddr,
Saurav Das822c4e22015-10-23 10:51:11 -0700638 IpPrefix.MAX_INET_MASK_LENGTH));
Charles Chan68aa62d2015-11-09 16:37:23 -0800639 tbuilder.setOutput(PortNumber.CONTROLLER);
640 puntIp.withSelector(sbuilder.build());
641 puntIp.withTreatment(tbuilder.build());
Saurav Das822c4e22015-10-23 10:51:11 -0700642 puntIp.withFlag(Flag.VERSATILE)
Charles Chan5270ed02016-01-30 23:22:37 -0800643 .withPriority(SegmentRoutingService.HIGHEST_PRIORITY)
Saurav Das822c4e22015-10-23 10:51:11 -0700644 .makePermanent()
645 .fromApp(srManager.appId);
Charles Chand2990362016-04-18 13:44:03 -0700646 ObjectiveContext context = new DefaultObjectiveContext(
647 (objective) -> log.debug("IP punt rule for {} populated", ipaddr),
648 (objective, error) ->
649 log.warn("Failed to populate IP punt rule for {}: {}", ipaddr, error));
650 srManager.flowObjectiveService.forward(deviceId, puntIp.add(context));
Saurav Das822c4e22015-10-23 10:51:11 -0700651 }
sanghob35a6192015-04-01 13:05:26 -0700652 }
653
Charles Chan68aa62d2015-11-09 16:37:23 -0800654 /**
655 * Populates a forwarding objective to send packets that miss other high
656 * priority Bridging Table entries to a group that contains all ports of
657 * its subnet.
658 *
659 * Note: We assume that packets sending from the edge switches to the hosts
660 * have untagged VLAN.
661 * The VLAN tag will be popped later in the flooding group.
662 *
663 * @param deviceId switch ID to set the rules
664 */
665 public void populateSubnetBroadcastRule(DeviceId deviceId) {
666 config.getSubnets(deviceId).forEach(subnet -> {
Charles Chand0fd5dc2016-02-16 23:14:49 -0800667 if (subnet.prefixLength() == 0 ||
668 subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH) {
669 return;
670 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800671 int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
672 VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
673
Saurav Das4ce45962015-11-24 23:21:05 -0800674 if (nextId < 0 || vlanId == null) {
Charles Chand0fd5dc2016-02-16 23:14:49 -0800675 log.error("Cannot install subnet {} broadcast rule in dev:{} due"
676 + "to vlanId:{} or nextId:{}", subnet, deviceId, vlanId, nextId);
Saurav Das4ce45962015-11-24 23:21:05 -0800677 return;
678 }
679
Charles Chan68aa62d2015-11-09 16:37:23 -0800680 /* Driver should treat objective with MacAddress.NONE as the
681 * subnet broadcast rule
682 */
683 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
684 sbuilder.matchVlanId(vlanId);
685 sbuilder.matchEthDst(MacAddress.NONE);
686
687 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
688 fob.withFlag(Flag.SPECIFIC)
689 .withSelector(sbuilder.build())
690 .nextStep(nextId)
Charles Chan5270ed02016-01-30 23:22:37 -0800691 .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
Charles Chan68aa62d2015-11-09 16:37:23 -0800692 .fromApp(srManager.appId)
693 .makePermanent();
Charles Chand2990362016-04-18 13:44:03 -0700694 ObjectiveContext context = new DefaultObjectiveContext(
695 (objective) -> log.debug("Subnet broadcast rule for {} populated", subnet),
696 (objective, error) ->
697 log.warn("Failed to populate subnet broadcast rule for {}: {}", subnet, error));
698 srManager.flowObjectiveService.forward(deviceId, fob.add(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800699 });
700 }
701
Charles Chan5270ed02016-01-30 23:22:37 -0800702 private int getPriorityFromPrefix(IpPrefix prefix) {
703 return (prefix.isIp4()) ?
704 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
705 500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700706 }
sanghob35a6192015-04-01 13:05:26 -0700707}