blob: 4e35571665fc9334fb7326913be6d18fdba58689 [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 Chan2df0e8a2017-01-09 11:45:08 -080029import org.onosproject.net.packet.PacketPriority;
Saurav Dasd2fded02016-12-02 15:43:47 -080030import org.onosproject.segmentrouting.DefaultRoutingHandler.PortFilterInfo;
Charles Chan0b4e6182015-11-03 10:42:14 -080031import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
32import org.onosproject.segmentrouting.config.DeviceConfiguration;
Charles Chan6ea94fc2016-05-10 17:29:47 -070033import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -070034import org.onosproject.segmentrouting.grouphandler.NeighborSet;
sanghob35a6192015-04-01 13:05:26 -070035import org.onosproject.net.DeviceId;
Saurav Das0e99e2b2015-10-28 12:39:42 -070036import org.onosproject.net.Port;
sanghob35a6192015-04-01 13:05:26 -070037import org.onosproject.net.PortNumber;
sanghob35a6192015-04-01 13:05:26 -070038import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
sanghob35a6192015-04-01 13:05:26 -070040import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070042import org.onosproject.net.flow.criteria.Criteria;
43import org.onosproject.net.flowobjective.DefaultFilteringObjective;
44import org.onosproject.net.flowobjective.DefaultForwardingObjective;
45import org.onosproject.net.flowobjective.FilteringObjective;
46import org.onosproject.net.flowobjective.ForwardingObjective;
47import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
Saurav Das822c4e22015-10-23 10:51:11 -070048import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
sanghob35a6192015-04-01 13:05:26 -070049import org.slf4j.Logger;
50import org.slf4j.LoggerFactory;
51
52import java.util.ArrayList;
Saurav Das837e0bb2015-10-30 17:45:38 -070053import java.util.HashSet;
sanghob35a6192015-04-01 13:05:26 -070054import java.util.List;
Charles Chan2df0e8a2017-01-09 11:45:08 -080055import java.util.Optional;
sanghob35a6192015-04-01 13:05:26 -070056import java.util.Set;
sangho20eff1d2015-04-13 15:15:58 -070057import java.util.concurrent.atomic.AtomicLong;
sanghob35a6192015-04-01 13:05:26 -070058
59import static com.google.common.base.Preconditions.checkNotNull;
60
Charles Chane849c192016-01-11 18:28:54 -080061/**
62 * Populator of segment routing flow rules.
63 */
sanghob35a6192015-04-01 13:05:26 -070064public class RoutingRulePopulator {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070065 private static final Logger log = LoggerFactory
66 .getLogger(RoutingRulePopulator.class);
sanghob35a6192015-04-01 13:05:26 -070067
sangho20eff1d2015-04-13 15:15:58 -070068 private AtomicLong rulePopulationCounter;
sangho666cd6d2015-04-14 16:27:13 -070069 private SegmentRoutingManager srManager;
70 private DeviceConfiguration config;
Saurav Das822c4e22015-10-23 10:51:11 -070071
sanghob35a6192015-04-01 13:05:26 -070072 /**
73 * Creates a RoutingRulePopulator object.
74 *
Thomas Vachuskae10f56b2015-04-15 18:20:08 -070075 * @param srManager segment routing manager reference
sanghob35a6192015-04-01 13:05:26 -070076 */
77 public RoutingRulePopulator(SegmentRoutingManager srManager) {
78 this.srManager = srManager;
sangho666cd6d2015-04-14 16:27:13 -070079 this.config = checkNotNull(srManager.deviceConfiguration);
sangho20eff1d2015-04-13 15:15:58 -070080 this.rulePopulationCounter = new AtomicLong(0);
81 }
82
83 /**
84 * Resets the population counter.
85 */
86 public void resetCounter() {
87 rulePopulationCounter.set(0);
88 }
89
90 /**
91 * Returns the number of rules populated.
Thomas Vachuska266b4432015-04-30 18:13:25 -070092 *
93 * @return number of rules
sangho20eff1d2015-04-13 15:15:58 -070094 */
95 public long getCounter() {
96 return rulePopulationCounter.get();
sanghob35a6192015-04-01 13:05:26 -070097 }
98
99 /**
Charles Chan1cdecff2016-10-27 14:19:48 -0700100 * Populates IP rules for a route that has direct connection to the
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700101 * switch.
sanghob35a6192015-04-01 13:05:26 -0700102 *
Charles Chan1cdecff2016-10-27 14:19:48 -0700103 * @param deviceId device ID of the device that next hop attaches to
104 * @param prefix IP prefix of the route
105 * @param hostMac MAC address of the next hop
106 * @param outPort port where the next hop attaches to
sanghob35a6192015-04-01 13:05:26 -0700107 */
Charles Chan1cdecff2016-10-27 14:19:48 -0700108 public void populateRoute(DeviceId deviceId, IpPrefix prefix,
sanghob35a6192015-04-01 13:05:26 -0700109 MacAddress hostMac, PortNumber outPort) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700110 log.debug("Populate IP table entry for route {} at {}:{}",
111 prefix, deviceId, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800112 ForwardingObjective.Builder fwdBuilder;
Charles Chan0b4e6182015-11-03 10:42:14 -0800113 try {
Charles Chan68aa62d2015-11-09 16:37:23 -0800114 fwdBuilder = getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700115 deviceId, prefix, hostMac, outPort);
Charles Chan0b4e6182015-11-03 10:42:14 -0800116 } catch (DeviceConfigNotFoundException e) {
117 log.warn(e.getMessage() + " Aborting populateIpRuleForHost.");
118 return;
119 }
Saurav Das59232cf2016-04-27 18:35:50 -0700120 if (fwdBuilder == null) {
121 log.warn("Aborting host routing table entries due "
Charles Chan1cdecff2016-10-27 14:19:48 -0700122 + "to error for dev:{} route:{}", deviceId, prefix);
Saurav Das59232cf2016-04-27 18:35:50 -0700123 return;
124 }
Charles Chand2990362016-04-18 13:44:03 -0700125 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan1cdecff2016-10-27 14:19:48 -0700126 (objective) -> log.debug("IP rule for route {} populated", prefix),
Charles Chand2990362016-04-18 13:44:03 -0700127 (objective, error) ->
Charles Chan1cdecff2016-10-27 14:19:48 -0700128 log.warn("Failed to populate IP rule for route {}: {}", prefix, error));
Charles Chand2990362016-04-18 13:44:03 -0700129 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800130 rulePopulationCounter.incrementAndGet();
131 }
132
Charles Chane849c192016-01-11 18:28:54 -0800133 /**
Charles Chan1cdecff2016-10-27 14:19:48 -0700134 * Removes IP rules for a route when the next hop is gone.
Charles Chane849c192016-01-11 18:28:54 -0800135 *
Charles Chan1cdecff2016-10-27 14:19:48 -0700136 * @param deviceId device ID of the device that next hop attaches to
137 * @param prefix IP prefix of the route
138 * @param hostMac MAC address of the next hop
139 * @param outPort port that next hop attaches to
Charles Chane849c192016-01-11 18:28:54 -0800140 */
Charles Chan1cdecff2016-10-27 14:19:48 -0700141 public void revokeRoute(DeviceId deviceId, IpPrefix prefix,
Charles Chan68aa62d2015-11-09 16:37:23 -0800142 MacAddress hostMac, PortNumber outPort) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700143 log.debug("Revoke IP table entry for route {} at {}:{}",
144 prefix, deviceId, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800145 ForwardingObjective.Builder fwdBuilder;
146 try {
147 fwdBuilder = getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700148 deviceId, prefix, hostMac, outPort);
Charles Chan68aa62d2015-11-09 16:37:23 -0800149 } catch (DeviceConfigNotFoundException e) {
150 log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
151 return;
152 }
Charles Chan458b8262016-11-30 11:55:05 -0800153 if (fwdBuilder == null) {
154 log.warn("Aborting host routing table entries due "
155 + "to error for dev:{} route:{}", deviceId, prefix);
156 return;
157 }
Charles Chand2990362016-04-18 13:44:03 -0700158 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan1cdecff2016-10-27 14:19:48 -0700159 (objective) -> log.debug("IP rule for route {} revoked", prefix),
Charles Chand2990362016-04-18 13:44:03 -0700160 (objective, error) ->
Charles Chan1cdecff2016-10-27 14:19:48 -0700161 log.warn("Failed to revoke IP rule for route {}: {}", prefix, error));
Charles Chand2990362016-04-18 13:44:03 -0700162 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800163 }
164
Charles Chan1cdecff2016-10-27 14:19:48 -0700165 /**
166 * Returns a forwarding objective that points packets destined to a
167 * given prefix to given port on given device with given destination MAC.
168 *
169 * @param deviceId device ID
170 * @param prefix prefix that need to be routed
171 * @param hostMac MAC address of the nexthop
172 * @param outPort port where the nexthop attaches to
173 * @return forwarding objective builder
174 * @throws DeviceConfigNotFoundException if given device is not configured
175 */
Charles Chan68aa62d2015-11-09 16:37:23 -0800176 private ForwardingObjective.Builder getForwardingObjectiveBuilder(
Charles Chan1cdecff2016-10-27 14:19:48 -0700177 DeviceId deviceId, IpPrefix prefix,
Charles Chan68aa62d2015-11-09 16:37:23 -0800178 MacAddress hostMac, PortNumber outPort)
179 throws DeviceConfigNotFoundException {
180 MacAddress deviceMac;
181 deviceMac = config.getDeviceMac(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800182
sanghob35a6192015-04-01 13:05:26 -0700183 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
sanghob35a6192015-04-01 13:05:26 -0700184 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan1cdecff2016-10-27 14:19:48 -0700185 sbuilder.matchIPDst(prefix);
Saurav Das4ce45962015-11-24 23:21:05 -0800186 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700187
Charles Chan1cdecff2016-10-27 14:19:48 -0700188 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
sangho1e575652015-05-14 00:39:53 -0700189 tbuilder.deferred()
190 .setEthDst(hostMac)
Charles Chan0b4e6182015-11-03 10:42:14 -0800191 .setEthSrc(deviceMac)
sanghob35a6192015-04-01 13:05:26 -0700192 .setOutput(outPort);
sanghob35a6192015-04-01 13:05:26 -0700193 TrafficTreatment treatment = tbuilder.build();
Saurav Das4ce45962015-11-24 23:21:05 -0800194
195 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
196 // for switch pipelines that need it, provide outgoing vlan as metadata
197 VlanId outvlan = null;
198 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outPort);
199 if (subnet == null) {
200 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
201 } else {
202 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
203 }
204 TrafficSelector meta = DefaultTrafficSelector.builder()
205 .matchVlanId(outvlan).build();
206 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
207 treatment, meta);
Saurav Das59232cf2016-04-27 18:35:50 -0700208 if (portNextObjId == -1) {
209 // warning log will come from getPortNextObjective method
210 return null;
211 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800212 return DefaultForwardingObjective.builder()
Saurav Das4ce45962015-11-24 23:21:05 -0800213 .withSelector(selector)
214 .nextStep(portNextObjId)
Charles Chan68aa62d2015-11-09 16:37:23 -0800215 .fromApp(srManager.appId).makePermanent()
Charles Chan1cdecff2016-10-27 14:19:48 -0700216 .withPriority(getPriorityFromPrefix(prefix))
Charles Chan5270ed02016-01-30 23:22:37 -0800217 .withFlag(ForwardingObjective.Flag.SPECIFIC);
sanghob35a6192015-04-01 13:05:26 -0700218 }
219
220 /**
221 * Populates IP flow rules for the subnets of the destination router.
222 *
223 * @param deviceId switch ID to set the rules
Charles Chan93e71ba2016-04-29 14:38:22 -0700224 * @param subnets subnet being added
sanghob35a6192015-04-01 13:05:26 -0700225 * @param destSw destination switch ID
226 * @param nextHops next hop switch ID list
227 * @return true if all rules are set successfully, false otherwise
228 */
Charles Chan93e71ba2016-04-29 14:38:22 -0700229 public boolean populateIpRuleForSubnet(DeviceId deviceId, Set<Ip4Prefix> subnets,
230 DeviceId destSw, Set<DeviceId> nextHops) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700231 for (IpPrefix subnet : subnets) {
sanghob35a6192015-04-01 13:05:26 -0700232 if (!populateIpRuleForRouter(deviceId, subnet, destSw, nextHops)) {
233 return false;
234 }
235 }
Charles Chan93e71ba2016-04-29 14:38:22 -0700236 return true;
237 }
sanghob35a6192015-04-01 13:05:26 -0700238
Charles Chan93e71ba2016-04-29 14:38:22 -0700239 /**
240 * Revokes IP flow rules for the subnets.
241 *
242 * @param subnets subnet being removed
243 * @return true if all rules are removed successfully, false otherwise
244 */
245 public boolean revokeIpRuleForSubnet(Set<Ip4Prefix> subnets) {
246 for (IpPrefix subnet : subnets) {
247 if (!revokeIpRuleForRouter(subnet)) {
248 return false;
249 }
250 }
sanghob35a6192015-04-01 13:05:26 -0700251 return true;
252 }
253
254 /**
Saurav Das25190812016-05-27 13:54:07 -0700255 * Populates IP flow rules for an IP prefix in the target device. The prefix
256 * is reachable via destination device.
sanghob35a6192015-04-01 13:05:26 -0700257 *
Saurav Dasa07f2032015-10-19 14:37:36 -0700258 * @param deviceId target device ID to set the rules
sanghob35a6192015-04-01 13:05:26 -0700259 * @param ipPrefix the IP address of the destination router
260 * @param destSw device ID of the destination router
261 * @param nextHops next hop switch ID list
262 * @return true if all rules are set successfully, false otherwise
263 */
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700264 public boolean populateIpRuleForRouter(DeviceId deviceId,
265 IpPrefix ipPrefix, DeviceId destSw,
266 Set<DeviceId> nextHops) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800267 int segmentId;
268 try {
269 segmentId = config.getSegmentId(destSw);
270 } catch (DeviceConfigNotFoundException e) {
271 log.warn(e.getMessage() + " Aborting populateIpRuleForRouter.");
272 return false;
273 }
sanghob35a6192015-04-01 13:05:26 -0700274
275 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
Charles Chan17d38f42016-02-05 13:33:54 -0800276 sbuilder.matchIPDst(ipPrefix);
sanghob35a6192015-04-01 13:05:26 -0700277 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800278 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700279
Charles Chan68aa62d2015-11-09 16:37:23 -0800280 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
281 NeighborSet ns;
282 TrafficTreatment treatment;
sanghob35a6192015-04-01 13:05:26 -0700283
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700284 // If the next hop is the same as the final destination, then MPLS label
285 // is not set.
sanghob35a6192015-04-01 13:05:26 -0700286 if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800287 tbuilder.immediate().decNwTtl();
sanghob35a6192015-04-01 13:05:26 -0700288 ns = new NeighborSet(nextHops);
Charles Chan68aa62d2015-11-09 16:37:23 -0800289 treatment = tbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700290 } else {
Charles Chan0b4e6182015-11-03 10:42:14 -0800291 ns = new NeighborSet(nextHops, segmentId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800292 treatment = null;
sanghob35a6192015-04-01 13:05:26 -0700293 }
294
Saurav Das8a0732e2015-11-20 15:27:53 -0800295 // setup metadata to pass to nextObjective - indicate the vlan on egress
296 // if needed by the switch pipeline. Since neighbor sets are always to
297 // other neighboring routers, there is no subnet assigned on those ports.
298 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
299 metabuilder.matchVlanId(
300 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
301
302 int nextId = srManager.getNextObjectiveId(deviceId, ns, metabuilder.build());
303 if (nextId <= 0) {
sangho834e4b02015-05-01 09:38:25 -0700304 log.warn("No next objective in {} for ns: {}", deviceId, ns);
305 return false;
306 }
307
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700308 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
309 .builder()
310 .fromApp(srManager.appId)
311 .makePermanent()
Saurav Das8a0732e2015-11-20 15:27:53 -0800312 .nextStep(nextId)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700313 .withSelector(selector)
Charles Chan5270ed02016-01-30 23:22:37 -0800314 .withPriority(getPriorityFromPrefix(ipPrefix))
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700315 .withFlag(ForwardingObjective.Flag.SPECIFIC);
Charles Chan68aa62d2015-11-09 16:37:23 -0800316 if (treatment != null) {
317 fwdBuilder.withTreatment(treatment);
318 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700319 log.debug("Installing IPv4 forwarding objective "
sangho834e4b02015-05-01 09:38:25 -0700320 + "for router IP/subnet {} in switch {}",
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700321 ipPrefix,
322 deviceId);
Charles Chand2990362016-04-18 13:44:03 -0700323 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das25190812016-05-27 13:54:07 -0700324 (objective) -> log.debug("IP rule for router {} populated in dev:{}",
325 ipPrefix, deviceId),
Charles Chand2990362016-04-18 13:44:03 -0700326 (objective, error) ->
Saurav Das25190812016-05-27 13:54:07 -0700327 log.warn("Failed to populate IP rule for router {}: {} in dev:{}",
328 ipPrefix, error, deviceId));
Charles Chand2990362016-04-18 13:44:03 -0700329 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
sangho20eff1d2015-04-13 15:15:58 -0700330 rulePopulationCounter.incrementAndGet();
sanghob35a6192015-04-01 13:05:26 -0700331
332 return true;
333 }
334
sanghob35a6192015-04-01 13:05:26 -0700335 /**
Charles Chan93e71ba2016-04-29 14:38:22 -0700336 * Revokes IP flow rules for the router IP address.
337 *
338 * @param ipPrefix the IP address of the destination router
339 * @return true if all rules are removed successfully, false otherwise
340 */
341 public boolean revokeIpRuleForRouter(IpPrefix ipPrefix) {
342 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
343 sbuilder.matchIPDst(ipPrefix);
344 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
345 TrafficSelector selector = sbuilder.build();
346 TrafficTreatment dummyTreatment = DefaultTrafficTreatment.builder().build();
347
348 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
349 .builder()
350 .fromApp(srManager.appId)
351 .makePermanent()
352 .withSelector(selector)
353 .withTreatment(dummyTreatment)
354 .withPriority(getPriorityFromPrefix(ipPrefix))
355 .withFlag(ForwardingObjective.Flag.SPECIFIC);
356
357 ObjectiveContext context = new DefaultObjectiveContext(
358 (objective) -> log.debug("IP rule for router {} revoked", ipPrefix),
359 (objective, error) ->
360 log.warn("Failed to revoke IP rule for router {}: {}", ipPrefix, error));
361
362 srManager.deviceService.getAvailableDevices().forEach(device -> {
363 srManager.flowObjectiveService.forward(device.id(), fwdBuilder.remove(context));
364 });
365
366 return true;
367 }
368
369 /**
Saurav Das25190812016-05-27 13:54:07 -0700370 * Populates MPLS flow rules in the target device to point towards the
371 * destination device.
sanghob35a6192015-04-01 13:05:26 -0700372 *
Saurav Das25190812016-05-27 13:54:07 -0700373 * @param targetSwId target device ID of the switch to set the rules
sanghob35a6192015-04-01 13:05:26 -0700374 * @param destSwId destination switch device ID
375 * @param nextHops next hops switch ID list
376 * @return true if all rules are set successfully, false otherwise
377 */
Saurav Das25190812016-05-27 13:54:07 -0700378 public boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700379 Set<DeviceId> nextHops) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800380 int segmentId;
381 try {
382 segmentId = config.getSegmentId(destSwId);
383 } catch (DeviceConfigNotFoundException e) {
384 log.warn(e.getMessage() + " Aborting populateMplsRule.");
385 return false;
386 }
sanghob35a6192015-04-01 13:05:26 -0700387
388 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
Sho SHIMIZU6cfc02d2015-09-11 11:19:11 -0700389 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
sanghob35a6192015-04-01 13:05:26 -0700390
391 // TODO Handle the case of Bos == false
sanghob35a6192015-04-01 13:05:26 -0700392 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
Saurav Das8a0732e2015-11-20 15:27:53 -0800393 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
Charles Chan188ebf52015-12-23 00:15:11 -0800394 sbuilder.matchMplsBos(true);
Saurav Das8a0732e2015-11-20 15:27:53 -0800395 TrafficSelector selector = sbuilder.build();
sanghob35a6192015-04-01 13:05:26 -0700396
Saurav Das8a0732e2015-11-20 15:27:53 -0800397 // setup metadata to pass to nextObjective - indicate the vlan on egress
398 // if needed by the switch pipeline. Since mpls next-hops are always to
399 // other neighboring routers, there is no subnet assigned on those ports.
400 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
401 metabuilder.matchVlanId(
402 VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
403
404 // If the next hop is the destination router for the segment, do pop
sanghob35a6192015-04-01 13:05:26 -0700405 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700406 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
Saurav Das25190812016-05-27 13:54:07 -0700407 + "label {} in switch {} with pop", segmentId, targetSwId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700408
Saurav Das8a0732e2015-11-20 15:27:53 -0800409 // bos pop case (php)
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700410 ForwardingObjective.Builder fwdObjBosBuilder =
Saurav Das25190812016-05-27 13:54:07 -0700411 getMplsForwardingObjective(targetSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700412 nextHops,
413 true,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700414 true,
Saurav Das8a0732e2015-11-20 15:27:53 -0800415 metabuilder.build());
416 if (fwdObjBosBuilder == null) {
sanghob35a6192015-04-01 13:05:26 -0700417 return false;
418 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800419 fwdObjBuilders.add(fwdObjBosBuilder);
420
421 // XXX not-bos pop case, SR app multi-label not implemented yet
422 /*ForwardingObjective.Builder fwdObjNoBosBuilder =
423 getMplsForwardingObjective(deviceId,
424 nextHops,
425 true,
426 false);*/
427
sanghob35a6192015-04-01 13:05:26 -0700428 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800429 // next hop is not destination, SR CONTINUE case (swap with self)
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700430 log.debug("Installing MPLS forwarding objective for "
Saurav Das25190812016-05-27 13:54:07 -0700431 + "label {} in switch {} without pop", segmentId, targetSwId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700432
Saurav Das8a0732e2015-11-20 15:27:53 -0800433 // continue case with bos - this does get triggered in edge routers
434 // and in core routers - driver can handle depending on availability
435 // of MPLS ECMP or not
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700436 ForwardingObjective.Builder fwdObjBosBuilder =
Saurav Das25190812016-05-27 13:54:07 -0700437 getMplsForwardingObjective(targetSwId,
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700438 nextHops,
439 false,
Saurav Das8a0732e2015-11-20 15:27:53 -0800440 true,
441 metabuilder.build());
442 if (fwdObjBosBuilder == null) {
sanghob35a6192015-04-01 13:05:26 -0700443 return false;
444 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800445 fwdObjBuilders.add(fwdObjBosBuilder);
446
447 // XXX continue case with not-bos - SR app multi label not implemented yet
448 // also requires MPLS ECMP
449 /*ForwardingObjective.Builder fwdObjNoBosBuilder =
450 getMplsForwardingObjective(deviceId,
451 nextHops,
452 false,
453 false); */
454
sanghob35a6192015-04-01 13:05:26 -0700455 }
Saurav Das25190812016-05-27 13:54:07 -0700456 // XXX when other cases above are implemented check for validity of
457 // debug messages below
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700458 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
459 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
460 .makePermanent()).withSelector(selector)
Charles Chan5270ed02016-01-30 23:22:37 -0800461 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700462 .withFlag(ForwardingObjective.Flag.SPECIFIC);
Charles Chand2990362016-04-18 13:44:03 -0700463 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das25190812016-05-27 13:54:07 -0700464 (objective) -> log.debug("MPLS rule {} for SID {} populated in dev:{} ",
465 objective.id(), segmentId, targetSwId),
Charles Chand2990362016-04-18 13:44:03 -0700466 (objective, error) ->
Saurav Das25190812016-05-27 13:54:07 -0700467 log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
468 objective.id(), segmentId, error, targetSwId));
469 ForwardingObjective fob = fwdObjBuilder.add(context);
470 log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
471 fob.id(), segmentId, fob.nextId(), targetSwId);
472 srManager.flowObjectiveService.forward(targetSwId, fob);
sangho20eff1d2015-04-13 15:15:58 -0700473 rulePopulationCounter.incrementAndGet();
sanghob35a6192015-04-01 13:05:26 -0700474 }
475
476 return true;
477 }
478
Saurav Das8a0732e2015-11-20 15:27:53 -0800479 private ForwardingObjective.Builder getMplsForwardingObjective(
480 DeviceId deviceId,
481 Set<DeviceId> nextHops,
482 boolean phpRequired,
483 boolean isBos,
484 TrafficSelector meta) {
485
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700486 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
487 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
sanghob35a6192015-04-01 13:05:26 -0700488
489 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
490
491 if (phpRequired) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800492 // php case - pop should always be flow-action
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700493 log.debug("getMplsForwardingObjective: php required");
sangho1e575652015-05-14 00:39:53 -0700494 tbuilder.deferred().copyTtlIn();
sanghob35a6192015-04-01 13:05:26 -0700495 if (isBos) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800496 tbuilder.deferred().popMpls(EthType.EtherType.IPV4.ethType())
497 .decNwTtl();
sanghob35a6192015-04-01 13:05:26 -0700498 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800499 tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
500 .decMplsTtl();
sanghob35a6192015-04-01 13:05:26 -0700501 }
502 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800503 // swap with self case - SR CONTINUE
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700504 log.debug("getMplsForwardingObjective: php not required");
sangho1e575652015-05-14 00:39:53 -0700505 tbuilder.deferred().decMplsTtl();
sanghob35a6192015-04-01 13:05:26 -0700506 }
507
Saurav Das8a0732e2015-11-20 15:27:53 -0800508 // All forwarding is via ECMP group, the metadata informs the driver
509 // that the next-Objective will be used by MPLS flows. In other words,
510 // MPLS ECMP is requested. It is up to the driver to decide if these
511 // packets will be hashed or not.
512 fwdBuilder.withTreatment(tbuilder.build());
513 NeighborSet ns = new NeighborSet(nextHops);
Saurav Das25190812016-05-27 13:54:07 -0700514 log.debug("Trying to get a nextObjId for mpls rule on device:{} to ns:{}",
Saurav Das8a0732e2015-11-20 15:27:53 -0800515 deviceId, ns);
516
517 int nextId = srManager.getNextObjectiveId(deviceId, ns, meta);
518 if (nextId <= 0) {
519 log.warn("No next objective in {} for ns: {}", deviceId, ns);
520 return null;
Saurav Das25190812016-05-27 13:54:07 -0700521 } else {
522 log.debug("nextObjId found:{} for mpls rule on device:{} to ns:{}",
523 nextId, deviceId, ns);
sanghob35a6192015-04-01 13:05:26 -0700524 }
525
Saurav Das8a0732e2015-11-20 15:27:53 -0800526 fwdBuilder.nextStep(nextId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700527 return fwdBuilder;
sanghob35a6192015-04-01 13:05:26 -0700528 }
529
530 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700531 * Creates a filtering objective to permit all untagged packets with a
Saurav Das0e99e2b2015-10-28 12:39:42 -0700532 * dstMac corresponding to the router's MAC address. For those pipelines
533 * that need to internally assign vlans to untagged packets, this method
534 * provides per-subnet vlan-ids as metadata.
Saurav Das837e0bb2015-10-30 17:45:38 -0700535 * <p>
536 * Note that the vlan assignment is only done by the master-instance for a switch.
537 * However we send the filtering objective from slave-instances as well, so
538 * that drivers can obtain other information (like Router MAC and IP).
sanghob35a6192015-04-01 13:05:26 -0700539 *
Saurav Das822c4e22015-10-23 10:51:11 -0700540 * @param deviceId the switch dpid for the router
Saurav Dasd2fded02016-12-02 15:43:47 -0800541 * @return PortFilterInfo information about the processed ports
sanghob35a6192015-04-01 13:05:26 -0700542 */
Saurav Dasd2fded02016-12-02 15:43:47 -0800543 public PortFilterInfo populateRouterMacVlanFilters(DeviceId deviceId) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700544 log.debug("Installing per-port filtering objective for untagged "
545 + "packets in device {}", deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800546
547 MacAddress deviceMac;
548 try {
549 deviceMac = config.getDeviceMac(deviceId);
550 } catch (DeviceConfigNotFoundException e) {
551 log.warn(e.getMessage() + " Aborting populateRouterMacVlanFilters.");
Saurav Dasd2fded02016-12-02 15:43:47 -0800552 return null;
Charles Chan0b4e6182015-11-03 10:42:14 -0800553 }
554
Saurav Das59232cf2016-04-27 18:35:50 -0700555 List<Port> devPorts = srManager.deviceService.getPorts(deviceId);
556 if (devPorts != null && devPorts.size() == 0) {
557 log.warn("Device {} ports not available. Unable to add MacVlan filters",
558 deviceId);
Saurav Dasd2fded02016-12-02 15:43:47 -0800559 return null;
Saurav Das59232cf2016-04-27 18:35:50 -0700560 }
Saurav Das25190812016-05-27 13:54:07 -0700561 int disabledPorts = 0, suppressedPorts = 0, filteredPorts = 0;
Saurav Das59232cf2016-04-27 18:35:50 -0700562 for (Port port : devPorts) {
Charles Chand2990362016-04-18 13:44:03 -0700563 ConnectPoint connectPoint = new ConnectPoint(deviceId, port.number());
Charles Chanf2565a92016-02-10 20:46:58 -0800564 // TODO: Handles dynamic port events when we are ready for dynamic config
Saurav Das25190812016-05-27 13:54:07 -0700565 if (!port.isEnabled()) {
566 disabledPorts++;
567 continue;
568 }
Charles Chan03a73e02016-10-24 14:52:01 -0700569
570 boolean isSuppressed = false;
571 SegmentRoutingAppConfig appConfig = srManager.cfgService
572 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Saurav Das25190812016-05-27 13:54:07 -0700573 if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
Charles Chan03a73e02016-10-24 14:52:01 -0700574 isSuppressed = true;
Saurav Das25190812016-05-27 13:54:07 -0700575 suppressedPorts++;
Saurav Das25190812016-05-27 13:54:07 -0700576 }
Charles Chan03a73e02016-10-24 14:52:01 -0700577
Saurav Das25190812016-05-27 13:54:07 -0700578 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
Charles Chan03a73e02016-10-24 14:52:01 -0700579 VlanId assignedVlan = (portSubnet == null || isSuppressed)
Saurav Das25190812016-05-27 13:54:07 -0700580 ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
581 : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
Charles Chan0b4e6182015-11-03 10:42:14 -0800582
Saurav Das25190812016-05-27 13:54:07 -0700583 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
584 fob.withKey(Criteria.matchInPort(port.number()))
Charles Chan0b4e6182015-11-03 10:42:14 -0800585 .addCondition(Criteria.matchEthDst(deviceMac))
Charles Chane849c192016-01-11 18:28:54 -0800586 .addCondition(Criteria.matchVlanId(VlanId.NONE))
Charles Chan5270ed02016-01-30 23:22:37 -0800587 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
Saurav Das25190812016-05-27 13:54:07 -0700588 // vlan assignment is valid only if this instance is master
589 if (srManager.mastershipService.isLocalMaster(deviceId)) {
590 TrafficTreatment tt = DefaultTrafficTreatment.builder()
591 .pushVlan().setVlanId(assignedVlan).build();
592 fob.withMeta(tt);
Saurav Das0e99e2b2015-10-28 12:39:42 -0700593 }
Saurav Das25190812016-05-27 13:54:07 -0700594 fob.permit().fromApp(srManager.appId);
595 log.debug("Sending filtering objective for dev/port:{}/{}", deviceId, port);
596 filteredPorts++;
597 ObjectiveContext context = new DefaultObjectiveContext(
598 (objective) -> log.debug("Filter for {} populated", connectPoint),
599 (objective, error) ->
600 log.warn("Failed to populate filter for {}: {}", connectPoint, error));
601 srManager.flowObjectiveService.filter(deviceId, fob.add(context));
Saurav Das0e99e2b2015-10-28 12:39:42 -0700602 }
Saurav Das25190812016-05-27 13:54:07 -0700603 log.info("Filtering on dev:{}, disabledPorts:{}, suppressedPorts:{}, filteredPorts:{}",
604 deviceId, disabledPorts, suppressedPorts, filteredPorts);
Saurav Dasd2fded02016-12-02 15:43:47 -0800605 return srManager.defaultRoutingHandler.new PortFilterInfo(disabledPorts,
606 suppressedPorts, filteredPorts);
sanghob35a6192015-04-01 13:05:26 -0700607 }
608
609 /**
Saurav Das822c4e22015-10-23 10:51:11 -0700610 * Creates a forwarding objective to punt all IP packets, destined to the
Saurav Das837e0bb2015-10-30 17:45:38 -0700611 * router's port IP addresses, to the controller. Note that the input
Saurav Das822c4e22015-10-23 10:51:11 -0700612 * port should not be matched on, as these packets can come from any input.
Saurav Das837e0bb2015-10-30 17:45:38 -0700613 * Furthermore, these are applied only by the master instance.
sanghob35a6192015-04-01 13:05:26 -0700614 *
Saurav Das822c4e22015-10-23 10:51:11 -0700615 * @param deviceId the switch dpid for the router
sanghob35a6192015-04-01 13:05:26 -0700616 */
Saurav Das822c4e22015-10-23 10:51:11 -0700617 public void populateRouterIpPunts(DeviceId deviceId) {
Charles Chan0b4e6182015-11-03 10:42:14 -0800618 Ip4Address routerIp;
619 try {
620 routerIp = config.getRouterIp(deviceId);
621 } catch (DeviceConfigNotFoundException e) {
622 log.warn(e.getMessage() + " Aborting populateRouterIpPunts.");
623 return;
624 }
625
Saurav Das837e0bb2015-10-30 17:45:38 -0700626 if (!srManager.mastershipService.isLocalMaster(deviceId)) {
627 log.debug("Not installing port-IP punts - not the master for dev:{} ",
628 deviceId);
629 return;
630 }
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 Chan2df0e8a2017-01-09 11:45:08 -0800634 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
635 .matchEthType(Ethernet.TYPE_IPV4)
636 .matchIPDst(IpPrefix.valueOf(ipaddr, IpPrefix.MAX_INET_MASK_LENGTH));
637 Optional<DeviceId> optDeviceId = Optional.of(deviceId);
638
639 srManager.packetService.requestPackets(sbuilder.build(),
640 PacketPriority.CONTROL, srManager.appId, optDeviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700641 }
sanghob35a6192015-04-01 13:05:26 -0700642 }
643
Charles Chan68aa62d2015-11-09 16:37:23 -0800644 /**
645 * Populates a forwarding objective to send packets that miss other high
646 * priority Bridging Table entries to a group that contains all ports of
647 * its subnet.
648 *
649 * Note: We assume that packets sending from the edge switches to the hosts
650 * have untagged VLAN.
651 * The VLAN tag will be popped later in the flooding group.
652 *
653 * @param deviceId switch ID to set the rules
654 */
655 public void populateSubnetBroadcastRule(DeviceId deviceId) {
656 config.getSubnets(deviceId).forEach(subnet -> {
Charles Chand0fd5dc2016-02-16 23:14:49 -0800657 if (subnet.prefixLength() == 0 ||
658 subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH) {
659 return;
660 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800661 int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
662 VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
663
Saurav Das4ce45962015-11-24 23:21:05 -0800664 if (nextId < 0 || vlanId == null) {
Charles Chand0fd5dc2016-02-16 23:14:49 -0800665 log.error("Cannot install subnet {} broadcast rule in dev:{} due"
666 + "to vlanId:{} or nextId:{}", subnet, deviceId, vlanId, nextId);
Saurav Das4ce45962015-11-24 23:21:05 -0800667 return;
668 }
669
Charles Chan68aa62d2015-11-09 16:37:23 -0800670 /* Driver should treat objective with MacAddress.NONE as the
671 * subnet broadcast rule
672 */
673 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
674 sbuilder.matchVlanId(vlanId);
675 sbuilder.matchEthDst(MacAddress.NONE);
676
677 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
678 fob.withFlag(Flag.SPECIFIC)
679 .withSelector(sbuilder.build())
680 .nextStep(nextId)
Charles Chan5270ed02016-01-30 23:22:37 -0800681 .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
Charles Chan68aa62d2015-11-09 16:37:23 -0800682 .fromApp(srManager.appId)
683 .makePermanent();
Charles Chand2990362016-04-18 13:44:03 -0700684 ObjectiveContext context = new DefaultObjectiveContext(
685 (objective) -> log.debug("Subnet broadcast rule for {} populated", subnet),
686 (objective, error) ->
687 log.warn("Failed to populate subnet broadcast rule for {}: {}", subnet, error));
688 srManager.flowObjectiveService.forward(deviceId, fob.add(context));
Charles Chan68aa62d2015-11-09 16:37:23 -0800689 });
690 }
691
Charles Chan5270ed02016-01-30 23:22:37 -0800692 private int getPriorityFromPrefix(IpPrefix prefix) {
693 return (prefix.isIp4()) ?
694 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
695 500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
Srikanth Vavilapallif3a8bc02015-05-22 13:47:31 -0700696 }
sanghob35a6192015-04-01 13:05:26 -0700697}