blob: 80666c5ba730005fea34418aeeef34a5240084ef [file] [log] [blame]
sangho80f11cb2015-04-01 13:05:26 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
sangho80f11cb2015-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
Pier Ventre229fd0b2016-10-31 16:49:19 -070018import com.google.common.collect.Lists;
Charles Chan051490d2018-01-11 11:48:18 -080019import com.google.common.collect.Sets;
Charles Chan61c086d2019-07-26 17:46:15 -070020import org.apache.commons.lang3.tuple.ImmutablePair;
Saurav Das4c35fc42015-11-20 15:27:53 -080021import org.onlab.packet.EthType;
sangho80f11cb2015-04-01 13:05:26 -070022import org.onlab.packet.Ethernet;
Charles Chanef8d12e2017-12-05 21:07:38 -080023import org.onlab.packet.IPv6;
sangho80f11cb2015-04-01 13:05:26 -070024import org.onlab.packet.Ip4Address;
Pier Ventreadb4ae62016-11-23 09:57:42 -080025import org.onlab.packet.Ip6Address;
26import org.onlab.packet.IpAddress;
sangho80f11cb2015-04-01 13:05:26 -070027import org.onlab.packet.IpPrefix;
28import org.onlab.packet.MacAddress;
sangho80f11cb2015-04-01 13:05:26 -070029import org.onlab.packet.MplsLabel;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070030import org.onlab.packet.VlanId;
Charles Chanb7f75ac2016-01-11 18:28:54 -080031import org.onosproject.net.ConnectPoint;
Saurav Das9bf49582018-08-13 15:34:26 -070032import org.onosproject.net.Device;
Charles Chanf17fade2020-03-08 18:07:19 -070033import org.onosproject.net.Host;
Charles Chan1eaf4802016-04-18 13:44:03 -070034import org.onosproject.net.flowobjective.DefaultObjectiveContext;
Pier Luigib9632ba2017-01-12 18:14:58 -080035import org.onosproject.net.flowobjective.Objective;
Charles Chan1eaf4802016-04-18 13:44:03 -070036import org.onosproject.net.flowobjective.ObjectiveContext;
Pier Luigib9632ba2017-01-12 18:14:58 -080037import org.onosproject.net.flowobjective.ObjectiveError;
Jonghwan Hyun671a7ab2018-04-30 09:27:21 -070038import org.onosproject.net.intf.Interface;
Charles Chan2d0bbcd2017-01-09 11:45:08 -080039import org.onosproject.net.packet.PacketPriority;
Charles Chan319d1a22015-11-03 10:42:14 -080040import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Saurav Das62ae6792017-05-15 15:34:25 -070041import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
Saurav Das261c3002017-06-13 15:35:54 -070042import org.onosproject.segmentrouting.grouphandler.DestinationSet;
sangho80f11cb2015-04-01 13:05:26 -070043import org.onosproject.net.DeviceId;
Saurav Das7c305372015-10-28 12:39:42 -070044import org.onosproject.net.Port;
sangho80f11cb2015-04-01 13:05:26 -070045import org.onosproject.net.PortNumber;
sangho80f11cb2015-04-01 13:05:26 -070046import org.onosproject.net.flow.DefaultTrafficSelector;
47import org.onosproject.net.flow.DefaultTrafficTreatment;
sangho80f11cb2015-04-01 13:05:26 -070048import org.onosproject.net.flow.TrafficSelector;
49import org.onosproject.net.flow.TrafficTreatment;
Srikanth Vavilapalli64505482015-04-21 13:04:13 -070050import org.onosproject.net.flow.criteria.Criteria;
51import org.onosproject.net.flowobjective.DefaultFilteringObjective;
52import org.onosproject.net.flowobjective.DefaultForwardingObjective;
53import org.onosproject.net.flowobjective.FilteringObjective;
54import org.onosproject.net.flowobjective.ForwardingObjective;
55import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
Saurav Das9f1c42e2015-10-23 10:51:11 -070056import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
sangho80f11cb2015-04-01 13:05:26 -070057import org.slf4j.Logger;
58import org.slf4j.LoggerFactory;
59
60import java.util.ArrayList;
Pier Ventre229fd0b2016-10-31 16:49:19 -070061import java.util.Collection;
62import java.util.Collections;
Saurav Das261c3002017-06-13 15:35:54 -070063import java.util.HashMap;
Saurav Dasc28b3432015-10-30 17:45:38 -070064import java.util.HashSet;
sangho80f11cb2015-04-01 13:05:26 -070065import java.util.List;
Saurav Das261c3002017-06-13 15:35:54 -070066import java.util.Map;
Charles Chan2d0bbcd2017-01-09 11:45:08 -080067import java.util.Optional;
sangho80f11cb2015-04-01 13:05:26 -070068import java.util.Set;
Charles Chan12a8a842020-02-14 13:23:57 -080069import java.util.concurrent.CompletableFuture;
pierventrea3989be2021-01-08 16:43:17 +010070import java.util.concurrent.ExecutionException;
sanghofb7c7292015-04-13 15:15:58 -070071import java.util.concurrent.atomic.AtomicLong;
Charles Chanf17f66b2018-02-26 21:33:25 -080072import java.util.stream.Collectors;
sangho80f11cb2015-04-01 13:05:26 -070073
74import static com.google.common.base.Preconditions.checkNotNull;
Pier Luigib9632ba2017-01-12 18:14:58 -080075import static org.onlab.packet.Ethernet.TYPE_ARP;
76import static org.onlab.packet.Ethernet.TYPE_IPV6;
Charles Chan051490d2018-01-11 11:48:18 -080077import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
Pier Luigib9632ba2017-01-12 18:14:58 -080078import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
Charles Chan051490d2018-01-11 11:48:18 -080079import static org.onlab.packet.ICMP6.ROUTER_ADVERTISEMENT;
80import static org.onlab.packet.ICMP6.ROUTER_SOLICITATION;
Pier Luigib9632ba2017-01-12 18:14:58 -080081import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
pierventree9261c92021-08-27 13:12:06 +020082import static org.onosproject.segmentrouting.metadata.SRObjectiveMetadata.CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES;
83import static org.onosproject.segmentrouting.metadata.SRObjectiveMetadata.INFRA_PORT;
84import static org.onosproject.segmentrouting.metadata.SRObjectiveMetadata.INTERFACE_CONFIG_UPDATE;
85import static org.onosproject.segmentrouting.metadata.SRObjectiveMetadata.PAIR_PORT;
86import static org.onosproject.segmentrouting.metadata.SRObjectiveMetadata.EDGE_PORT;
sangho80f11cb2015-04-01 13:05:26 -070087
Charles Chanb7f75ac2016-01-11 18:28:54 -080088/**
89 * Populator of segment routing flow rules.
90 */
sangho80f11cb2015-04-01 13:05:26 -070091public class RoutingRulePopulator {
Charles Chanef8d12e2017-12-05 21:07:38 -080092 private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
93
94 private static final int ARP_NDP_PRIORITY = 30000;
sangho80f11cb2015-04-01 13:05:26 -070095
sanghofb7c7292015-04-13 15:15:58 -070096 private AtomicLong rulePopulationCounter;
sangho9b169e32015-04-14 16:27:13 -070097 private SegmentRoutingManager srManager;
98 private DeviceConfiguration config;
psneha86e60d32019-03-26 06:31:41 -040099 private RouteSimplifierUtils routeSimplifierUtils;
Saurav Das9f1c42e2015-10-23 10:51:11 -0700100
pierventrea3989be2021-01-08 16:43:17 +0100101 private static final long METADATA_MASK = 0xffffffffffffffffL;
Andreas Pantelopoulos59bd97e2018-06-28 17:06:14 -0700102
sangho80f11cb2015-04-01 13:05:26 -0700103 /**
104 * Creates a RoutingRulePopulator object.
105 *
Thomas Vachuska8a075092015-04-15 18:20:08 -0700106 * @param srManager segment routing manager reference
sangho80f11cb2015-04-01 13:05:26 -0700107 */
Charles Chan3ed34d82017-06-22 18:03:14 -0700108 RoutingRulePopulator(SegmentRoutingManager srManager) {
sangho80f11cb2015-04-01 13:05:26 -0700109 this.srManager = srManager;
sangho9b169e32015-04-14 16:27:13 -0700110 this.config = checkNotNull(srManager.deviceConfiguration);
sanghofb7c7292015-04-13 15:15:58 -0700111 this.rulePopulationCounter = new AtomicLong(0);
psneha86e60d32019-03-26 06:31:41 -0400112 this.routeSimplifierUtils = new RouteSimplifierUtils(srManager);
sanghofb7c7292015-04-13 15:15:58 -0700113 }
114
115 /**
116 * Resets the population counter.
117 */
Charles Chan3ed34d82017-06-22 18:03:14 -0700118 void resetCounter() {
sanghofb7c7292015-04-13 15:15:58 -0700119 rulePopulationCounter.set(0);
120 }
121
122 /**
123 * Returns the number of rules populated.
Thomas Vachuska7cfc6202015-04-30 18:13:25 -0700124 *
125 * @return number of rules
sanghofb7c7292015-04-13 15:15:58 -0700126 */
Charles Chan3ed34d82017-06-22 18:03:14 -0700127 long getCounter() {
sanghofb7c7292015-04-13 15:15:58 -0700128 return rulePopulationCounter.get();
sangho80f11cb2015-04-01 13:05:26 -0700129 }
130
131 /**
Charles Chand66d6712018-03-29 16:03:41 -0700132 * Populate a bridging rule on given deviceId that matches given mac, given vlan and
133 * output to given port.
134 *
135 * @param deviceId device ID
136 * @param port port
137 * @param mac mac address
138 * @param vlanId VLAN ID
Charles Chan12a8a842020-02-14 13:23:57 -0800139 * @return future that carries the flow objective if succeeded, null if otherwise
Charles Chand66d6712018-03-29 16:03:41 -0700140 */
Charles Chan12a8a842020-02-14 13:23:57 -0800141 CompletableFuture<Objective> populateBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
Charles Chand66d6712018-03-29 16:03:41 -0700142 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, false);
143 if (fob == null) {
144 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
Charles Chan12a8a842020-02-14 13:23:57 -0800145 return CompletableFuture.completedFuture(null);
Charles Chand66d6712018-03-29 16:03:41 -0700146 }
147
Charles Chan12a8a842020-02-14 13:23:57 -0800148 CompletableFuture<Objective> future = new CompletableFuture<>();
Charles Chand66d6712018-03-29 16:03:41 -0700149 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan12a8a842020-02-14 13:23:57 -0800150 (objective) -> {
151 log.debug("Brigding rule for {}/{} populated", mac, vlanId);
152 future.complete(objective);
153 },
154 (objective, error) -> {
155 log.warn("Failed to populate bridging rule for {}/{}: {}", mac, vlanId, error);
156 future.complete(null);
157 });
Charles Chand66d6712018-03-29 16:03:41 -0700158 srManager.flowObjectiveService.forward(deviceId, fob.add(context));
Charles Chan12a8a842020-02-14 13:23:57 -0800159 return future;
Charles Chand66d6712018-03-29 16:03:41 -0700160 }
161
162 /**
163 * Revoke a bridging rule on given deviceId that matches given mac, given vlan and
164 * output to given port.
165 *
166 * @param deviceId device ID
167 * @param port port
168 * @param mac mac address
169 * @param vlanId VLAN ID
Charles Chan12a8a842020-02-14 13:23:57 -0800170 * @return future that carries the flow objective if succeeded, null if otherwise
Charles Chand66d6712018-03-29 16:03:41 -0700171 */
Charles Chan12a8a842020-02-14 13:23:57 -0800172 CompletableFuture<Objective> revokeBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
Charles Chand66d6712018-03-29 16:03:41 -0700173 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, true);
174 if (fob == null) {
175 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
Charles Chan12a8a842020-02-14 13:23:57 -0800176 return CompletableFuture.completedFuture(null);
Charles Chand66d6712018-03-29 16:03:41 -0700177 }
178
Charles Chan12a8a842020-02-14 13:23:57 -0800179 CompletableFuture<Objective> future = new CompletableFuture<>();
Charles Chand66d6712018-03-29 16:03:41 -0700180 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan12a8a842020-02-14 13:23:57 -0800181 (objective) -> {
182 log.debug("Brigding rule for {}/{} revoked", mac, vlanId);
183 future.complete(objective);
184 },
185 (objective, error) -> {
186 log.warn("Failed to revoke bridging rule for {}/{}: {}", mac, vlanId, error);
187 future.complete(null);
188 });
Charles Chand66d6712018-03-29 16:03:41 -0700189 srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
Charles Chan12a8a842020-02-14 13:23:57 -0800190 return future;
Charles Chand66d6712018-03-29 16:03:41 -0700191 }
192
193 /**
194 * Generates a forwarding objective builder for bridging rules.
195 * <p>
196 * The forwarding objective bridges packets destined to a given MAC to
197 * given port on given device.
198 *
199 * @param deviceId Device that host attaches to
200 * @param mac MAC address of the host
201 * @param hostVlanId VLAN ID of the host
202 * @param outport Port that host attaches to
203 * @param revoke true if forwarding objective is meant to revoke forwarding rule
204 * @return Forwarding objective builder
205 */
206 private ForwardingObjective.Builder bridgingFwdObjBuilder(
207 DeviceId deviceId, MacAddress mac, VlanId hostVlanId, PortNumber outport, boolean revoke) {
208 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
Charles Chan098ca202018-05-01 11:50:20 -0700209 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
210 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
211 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
Charles Chand66d6712018-03-29 16:03:41 -0700212
213 // Create host selector
214 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
215 sbuilder.matchEthDst(mac);
216
217 // Create host treatment
218 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
219 tbuilder.immediate().setOutput(outport);
220
221 // Create host meta
222 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
223
224 // Adjust the selector, treatment and meta according to VLAN configuration
225 if (taggedVlans.contains(hostVlanId)) {
226 sbuilder.matchVlanId(hostVlanId);
227 mbuilder.matchVlanId(hostVlanId);
228 } else if (hostVlanId.equals(VlanId.NONE)) {
229 if (untaggedVlan != null) {
230 sbuilder.matchVlanId(untaggedVlan);
231 mbuilder.matchVlanId(untaggedVlan);
232 tbuilder.immediate().popVlan();
233 } else if (nativeVlan != null) {
234 sbuilder.matchVlanId(nativeVlan);
235 mbuilder.matchVlanId(nativeVlan);
236 tbuilder.immediate().popVlan();
237 } else {
pierventrea3989be2021-01-08 16:43:17 +0100238 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native " +
Charles Chand66d6712018-03-29 16:03:41 -0700239 "vlan config", mac, hostVlanId, connectPoint);
240 return null;
241 }
242 } else {
243 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
244 mac, hostVlanId, connectPoint);
245 return null;
246 }
247
248 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
249 // If the objective is to revoke an existing rule, and for some reason
250 // the next-objective does not exist, then a new one should not be created
251 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
252 tbuilder.build(), mbuilder.build(), !revoke);
253 if (portNextObjId == -1) {
254 // Warning log will come from getPortNextObjective method
255 return null;
256 }
257
258 return DefaultForwardingObjective.builder()
259 .withFlag(ForwardingObjective.Flag.SPECIFIC)
260 .withSelector(sbuilder.build())
261 .nextStep(portNextObjId)
262 .withPriority(100)
263 .fromApp(srManager.appId)
264 .makePermanent();
265 }
266
267 /**
268 * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
269 * and hostMAC connected to given port, and output to given port only when
270 * vlan information is valid.
271 *
272 * @param deviceId device ID that host attaches to
273 * @param portNum port number that host attaches to
274 * @param hostMac mac address of the host connected to the switch port
275 * @param vlanId Vlan ID configured on the switch port
276 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
277 * @param install true to populate the objective, false to revoke
pierventrea3989be2021-01-08 16:43:17 +0100278 * @return a completable future that completes when the update of the bridging rule completes
Charles Chand66d6712018-03-29 16:03:41 -0700279 */
280 // TODO Refactor. There are a lot of duplications between this method, populateBridging,
281 // revokeBridging and bridgingFwdObjBuilder.
pierventrea3989be2021-01-08 16:43:17 +0100282 CompletableFuture<Objective> updateBridging(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
Charles Chand66d6712018-03-29 16:03:41 -0700283 VlanId vlanId, boolean popVlan, boolean install) {
284 // Create host selector
285 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
286 sbuilder.matchEthDst(hostMac);
287
288 // Create host meta
289 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
290
291 sbuilder.matchVlanId(vlanId);
292 mbuilder.matchVlanId(vlanId);
293
294 // Create host treatment
295 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
296 tbuilder.immediate().setOutput(portNum);
297
298 if (popVlan) {
299 tbuilder.immediate().popVlan();
300 }
301
302 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
303 tbuilder.build(), mbuilder.build(), install);
pierventrea3989be2021-01-08 16:43:17 +0100304 CompletableFuture<Objective> future = new CompletableFuture<>();
Charles Chand66d6712018-03-29 16:03:41 -0700305 if (portNextObjId != -1) {
306 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
307 .withFlag(ForwardingObjective.Flag.SPECIFIC)
308 .withSelector(sbuilder.build())
309 .nextStep(portNextObjId)
310 .withPriority(100)
311 .fromApp(srManager.appId)
312 .makePermanent();
313
314 ObjectiveContext context = new DefaultObjectiveContext(
pierventrea3989be2021-01-08 16:43:17 +0100315 (objective) -> {
316 log.debug("Brigding rule for {}/{} {}", hostMac, vlanId, install ? "populated" : "revoked");
317 future.complete(objective);
318 },
319 (objective, error) -> {
320 log.warn("Failed to {} bridging rule for {}/{}: {}", install ? "populate" : "revoke",
321 hostMac, vlanId, error);
322 future.complete(null);
323 });
Charles Chand66d6712018-03-29 16:03:41 -0700324 srManager.flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
325 } else {
326 log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
pierventrea3989be2021-01-08 16:43:17 +0100327 return CompletableFuture.completedFuture(null);
Charles Chand66d6712018-03-29 16:03:41 -0700328 }
pierventrea3989be2021-01-08 16:43:17 +0100329 return future;
Charles Chand66d6712018-03-29 16:03:41 -0700330 }
331
332 /**
333 * Populates IP rules for a route that has direct connection to the switch.
334 * This method should not be invoked directly without going through DefaultRoutingHandler.
sangho80f11cb2015-04-01 13:05:26 -0700335 *
Charles Chanddac7fd2016-10-27 14:19:48 -0700336 * @param deviceId device ID of the device that next hop attaches to
337 * @param prefix IP prefix of the route
338 * @param hostMac MAC address of the next hop
Charles Chan90772a72017-02-08 15:52:08 -0800339 * @param hostVlanId Vlan ID of the nexthop
Charles Chanddac7fd2016-10-27 14:19:48 -0700340 * @param outPort port where the next hop attaches to
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000341 * @param directHost host is of type direct or indirect
Charles Chan12a8a842020-02-14 13:23:57 -0800342 * @return future that carries the flow objective if succeeded, null if otherwise
sangho80f11cb2015-04-01 13:05:26 -0700343 */
Charles Chan12a8a842020-02-14 13:23:57 -0800344 CompletableFuture<Objective> populateRoute(DeviceId deviceId, IpPrefix prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000345 MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
Saurav Das261c3002017-06-13 15:35:54 -0700346 log.debug("Populate direct routing entry for route {} at {}:{}",
Charles Chanddac7fd2016-10-27 14:19:48 -0700347 prefix, deviceId, outPort);
Charles Chanf4586112015-11-09 16:37:23 -0800348 ForwardingObjective.Builder fwdBuilder;
Charles Chan319d1a22015-11-03 10:42:14 -0800349 try {
Saurav Das2cb38292017-03-29 19:09:17 -0700350 fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
Charles Chan61c086d2019-07-26 17:46:15 -0700351 hostVlanId, outPort, null, null, directHost, false);
Charles Chan319d1a22015-11-03 10:42:14 -0800352 } catch (DeviceConfigNotFoundException e) {
Saurav Das261c3002017-06-13 15:35:54 -0700353 log.warn(e.getMessage() + " Aborting direct populateRoute");
Charles Chan12a8a842020-02-14 13:23:57 -0800354 return CompletableFuture.completedFuture(null);
Charles Chan319d1a22015-11-03 10:42:14 -0800355 }
Saurav Das07c74602016-04-27 18:35:50 -0700356 if (fwdBuilder == null) {
Saurav Das368cf212017-03-15 15:15:14 -0700357 log.warn("Aborting host routing table entry due "
Charles Chanddac7fd2016-10-27 14:19:48 -0700358 + "to error for dev:{} route:{}", deviceId, prefix);
Charles Chan12a8a842020-02-14 13:23:57 -0800359 return CompletableFuture.completedFuture(null);
Saurav Das07c74602016-04-27 18:35:50 -0700360 }
Charles Chan910be6a2017-08-23 14:46:43 -0700361
362 int nextId = fwdBuilder.add().nextId();
Charles Chan12a8a842020-02-14 13:23:57 -0800363 CompletableFuture<Objective> future = new CompletableFuture<>();
Charles Chan1eaf4802016-04-18 13:44:03 -0700364 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan12a8a842020-02-14 13:23:57 -0800365 (objective) -> {
366 log.debug("Direct routing rule for route {} populated. nextId={}", prefix, nextId);
367 future.complete(objective);
368 },
369 (objective, error) -> {
370 log.warn("Failed to populate direct routing rule for route {}: {}", prefix, error);
371 future.complete(null);
372 });
Charles Chan1eaf4802016-04-18 13:44:03 -0700373 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
Charles Chanf4586112015-11-09 16:37:23 -0800374 rulePopulationCounter.incrementAndGet();
Charles Chan12a8a842020-02-14 13:23:57 -0800375 return future;
Charles Chanf4586112015-11-09 16:37:23 -0800376 }
377
Charles Chanb7f75ac2016-01-11 18:28:54 -0800378 /**
Charles Chanddac7fd2016-10-27 14:19:48 -0700379 * Removes IP rules for a route when the next hop is gone.
Charles Chand66d6712018-03-29 16:03:41 -0700380 * This method should not be invoked directly without going through DefaultRoutingHandler.
Charles Chanb7f75ac2016-01-11 18:28:54 -0800381 *
Charles Chanddac7fd2016-10-27 14:19:48 -0700382 * @param deviceId device ID of the device that next hop attaches to
383 * @param prefix IP prefix of the route
384 * @param hostMac MAC address of the next hop
Charles Chan90772a72017-02-08 15:52:08 -0800385 * @param hostVlanId Vlan ID of the nexthop
Charles Chanddac7fd2016-10-27 14:19:48 -0700386 * @param outPort port that next hop attaches to
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000387 * @param directHost host is of type direct or indirect
Charles Chan12a8a842020-02-14 13:23:57 -0800388 * @return future that carries the flow objective if succeeded, null if otherwise
Charles Chanb7f75ac2016-01-11 18:28:54 -0800389 */
Charles Chan12a8a842020-02-14 13:23:57 -0800390 CompletableFuture<Objective> revokeRoute(DeviceId deviceId, IpPrefix prefix,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000391 MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700392 log.debug("Revoke IP table entry for route {} at {}:{}",
393 prefix, deviceId, outPort);
Charles Chanf4586112015-11-09 16:37:23 -0800394 ForwardingObjective.Builder fwdBuilder;
395 try {
Saurav Das2cb38292017-03-29 19:09:17 -0700396 fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
Charles Chan61c086d2019-07-26 17:46:15 -0700397 hostVlanId, outPort, null, null, directHost, true);
Charles Chanf4586112015-11-09 16:37:23 -0800398 } catch (DeviceConfigNotFoundException e) {
399 log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
Charles Chan12a8a842020-02-14 13:23:57 -0800400 return CompletableFuture.completedFuture(null);
Charles Chanf4586112015-11-09 16:37:23 -0800401 }
Charles Chanea702b12016-11-30 11:55:05 -0800402 if (fwdBuilder == null) {
403 log.warn("Aborting host routing table entries due "
404 + "to error for dev:{} route:{}", deviceId, prefix);
Charles Chan12a8a842020-02-14 13:23:57 -0800405 return CompletableFuture.completedFuture(null);
Charles Chanea702b12016-11-30 11:55:05 -0800406 }
Charles Chan12a8a842020-02-14 13:23:57 -0800407
408 CompletableFuture<Objective> future = new CompletableFuture<>();
Charles Chan1eaf4802016-04-18 13:44:03 -0700409 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan12a8a842020-02-14 13:23:57 -0800410 (objective) -> {
411 log.debug("IP rule for route {} revoked", prefix);
412 future.complete(objective);
413 },
414 (objective, error) -> {
415 log.warn("Failed to revoke IP rule for route {}: {}", prefix, error);
416 future.complete(null);
417 });
Charles Chan1eaf4802016-04-18 13:44:03 -0700418 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
Charles Chan12a8a842020-02-14 13:23:57 -0800419 return future;
Charles Chanf4586112015-11-09 16:37:23 -0800420 }
421
Charles Chanddac7fd2016-10-27 14:19:48 -0700422 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800423 * Returns a forwarding objective builder for routing rules.
424 * <p>
425 * The forwarding objective routes packets destined to a given prefix to
426 * given port on given device with given destination MAC.
Charles Chanddac7fd2016-10-27 14:19:48 -0700427 *
428 * @param deviceId device ID
429 * @param prefix prefix that need to be routed
430 * @param hostMac MAC address of the nexthop
Charles Chan90772a72017-02-08 15:52:08 -0800431 * @param hostVlanId Vlan ID of the nexthop
Charles Chanddac7fd2016-10-27 14:19:48 -0700432 * @param outPort port where the nexthop attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700433 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000434 * @param directHost host is direct or indirect
Charles Chanddac7fd2016-10-27 14:19:48 -0700435 * @return forwarding objective builder
436 * @throws DeviceConfigNotFoundException if given device is not configured
437 */
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000438
Charles Chan61c086d2019-07-26 17:46:15 -0700439 private ForwardingObjective.Builder routingFwdObjBuilder(
Charles Chanddac7fd2016-10-27 14:19:48 -0700440 DeviceId deviceId, IpPrefix prefix,
Saurav Das2cb38292017-03-29 19:09:17 -0700441 MacAddress hostMac, VlanId hostVlanId, PortNumber outPort,
Charles Chan61c086d2019-07-26 17:46:15 -0700442 VlanId innerVlan, EthType outerTpid,
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000443 boolean directHost, boolean revoke)
Charles Chanf4586112015-11-09 16:37:23 -0800444 throws DeviceConfigNotFoundException {
Charles Chan61c086d2019-07-26 17:46:15 -0700445 int nextObjId;
446 if (directHost) {
447 // if the objective is to revoke an existing rule, and for some reason
448 // the next-objective does not exist, then a new one should not be created
449 ImmutablePair<TrafficTreatment, TrafficSelector> treatmentAndMeta =
450 getTreatmentAndMeta(deviceId, hostMac, hostVlanId, outPort, innerVlan, outerTpid);
451 if (treatmentAndMeta == null) {
452 // Warning log will come from getTreatmentAndMeta method
453 return null;
454 }
455 nextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
456 treatmentAndMeta.getLeft(), treatmentAndMeta.getRight(), !revoke);
457 } else {
458 // if the objective is to revoke an existing rule, and for some reason
459 // the next-objective does not exist, then a new one should not be created
460 nextObjId = srManager.getMacVlanNextObjectiveId(deviceId, hostMac, hostVlanId,
461 outPort, !revoke);
462 }
463 if (nextObjId == -1) {
464 // Warning log will come from getMacVlanNextObjective method
465 return null;
466 }
467
468 return DefaultForwardingObjective.builder()
469 .withSelector(buildIpSelectorFromIpPrefix(prefix).build())
470 .nextStep(nextObjId)
471 .fromApp(srManager.appId).makePermanent()
472 .withPriority(getPriorityFromPrefix(prefix))
473 .withFlag(ForwardingObjective.Flag.SPECIFIC);
474 }
475
476 private ImmutablePair<TrafficTreatment, TrafficSelector> getTreatmentAndMeta(
477 DeviceId deviceId, MacAddress hostMac, VlanId hostVlanId, PortNumber outPort,
478 VlanId innerVlan, EthType outerTpid)
479 throws DeviceConfigNotFoundException {
480 MacAddress routerMac;
481 routerMac = config.getDeviceMac(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800482
Charles Chan90772a72017-02-08 15:52:08 -0800483 ConnectPoint connectPoint = new ConnectPoint(deviceId, outPort);
Charles Chan098ca202018-05-01 11:50:20 -0700484 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
485 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
486 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
sangho80f11cb2015-04-01 13:05:26 -0700487
Charles Chan90772a72017-02-08 15:52:08 -0800488 // Create route treatment
pierventre50e1bb82020-12-14 19:31:03 +0100489 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
490 .deferred()
sangho27462c62015-05-14 00:39:53 -0700491 .setEthDst(hostMac)
Charles Chan61c086d2019-07-26 17:46:15 -0700492 .setEthSrc(routerMac)
sangho80f11cb2015-04-01 13:05:26 -0700493 .setOutput(outPort);
Saurav Das2d94d312015-11-24 23:21:05 -0800494
Charles Chan90772a72017-02-08 15:52:08 -0800495 // Create route meta
496 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
Charles Chan10b0fb72017-02-02 16:20:42 -0800497
Charles Chan61c086d2019-07-26 17:46:15 -0700498 // Adjust treatment and meta according to VLAN configuration
Charles Chan90772a72017-02-08 15:52:08 -0800499 if (taggedVlans.contains(hostVlanId)) {
pierventre50e1bb82020-12-14 19:31:03 +0100500 mbuilder.matchVlanId(hostVlanId);
Charles Chan90772a72017-02-08 15:52:08 -0800501 tbuilder.setVlanId(hostVlanId);
502 } else if (hostVlanId.equals(VlanId.NONE)) {
503 if (untaggedVlan != null) {
504 mbuilder.matchVlanId(untaggedVlan);
pierventre50e1bb82020-12-14 19:31:03 +0100505 tbuilder.popVlan();
Charles Chan90772a72017-02-08 15:52:08 -0800506 } else if (nativeVlan != null) {
507 mbuilder.matchVlanId(nativeVlan);
pierventre50e1bb82020-12-14 19:31:03 +0100508 tbuilder.popVlan();
Charles Chan90772a72017-02-08 15:52:08 -0800509 } else {
Charles Chan3ed34d82017-06-22 18:03:14 -0700510 log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
511 hostMac, hostVlanId, connectPoint);
512 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800513 }
514 } else {
Charles Chan61c086d2019-07-26 17:46:15 -0700515 // Double tagged hosts
516 if (innerVlan == null || outerTpid == null) {
517 log.warn("Failed to construct NextObj for double tagged hosts {}/{}. {} {}",
518 hostMac, hostVlanId,
519 (innerVlan == null) ? "innerVlan = null." : "",
520 (outerTpid == null) ? "outerTpid = null." : "");
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -0700521 return null;
522 }
Charles Chan61c086d2019-07-26 17:46:15 -0700523 tbuilder.setVlanId(innerVlan);
524 tbuilder.pushVlan(outerTpid);
525 tbuilder.setVlanId(hostVlanId);
526 mbuilder.matchVlanId(VlanId.ANY);
Saurav Das07c74602016-04-27 18:35:50 -0700527 }
Ruchi Sahota71bcb4e2019-01-28 01:08:18 +0000528
Charles Chan61c086d2019-07-26 17:46:15 -0700529 return ImmutablePair.of(tbuilder.build(), mbuilder.build());
sangho80f11cb2015-04-01 13:05:26 -0700530 }
531
532 /**
Saurav Das261c3002017-06-13 15:35:54 -0700533 * Populates IP flow rules for all the given prefixes reachable from the
534 * destination switch(es).
sangho80f11cb2015-04-01 13:05:26 -0700535 *
Saurav Das261c3002017-06-13 15:35:54 -0700536 * @param targetSw switch where rules are to be programmed
537 * @param subnets subnets/prefixes being added
538 * @param destSw1 destination switch where the prefixes are reachable
539 * @param destSw2 paired destination switch if one exists for the subnets/prefixes.
540 * Should be null if there is no paired destination switch (by config)
541 * or if the given prefixes are reachable only via destSw1
542 * @param nextHops a map containing a set of next-hops for each destination switch.
543 * If destSw2 is not null, then this map must contain an
544 * entry for destSw2 with its next-hops from the targetSw
545 * (although the next-hop set may be empty in certain scenarios).
546 * If destSw2 is null, there should not be an entry in this
547 * map for destSw2.
sangho80f11cb2015-04-01 13:05:26 -0700548 * @return true if all rules are set successfully, false otherwise
549 */
Charles Chan3ed34d82017-06-22 18:03:14 -0700550 boolean populateIpRuleForSubnet(DeviceId targetSw, Set<IpPrefix> subnets,
Saurav Das261c3002017-06-13 15:35:54 -0700551 DeviceId destSw1, DeviceId destSw2, Map<DeviceId, Set<DeviceId>> nextHops) {
psneha86e60d32019-03-26 06:31:41 -0400552 // Get pair device of the target switch
553 Optional<DeviceId> pairDev = srManager.getPairDeviceId(targetSw);
554 // Route simplification will be off in case of the nexthop location at target switch is down
555 // (routing through spine case)
556 boolean routeSimplOff = pairDev.isPresent() && pairDev.get().equals(destSw1) && destSw2 == null;
piera9941192019-04-24 16:12:47 +0200557 // Iterates over the routes. Checking:
psneha86e60d32019-03-26 06:31:41 -0400558 // If route simplification is enabled
559 // If the target device is another leaf in the network
560 if (srManager.routeSimplification && !routeSimplOff) {
piera9941192019-04-24 16:12:47 +0200561 Set<IpPrefix> subnetsToBePopulated = Sets.newHashSet();
psneha86e60d32019-03-26 06:31:41 -0400562 for (IpPrefix subnet : subnets) {
563 // Skip route programming on the target device
564 // If route simplification applies
565 if (routeSimplifierUtils.hasLeafExclusionEnabledForPrefix(subnet)) {
566 // XXX route simplification assumes that source of the traffic
567 // towards the nexthops are co-located with the nexthops. In different
568 // scenarios will not work properly.
569 continue;
570 }
571 // populate the route in the remaning scenarios
piera9941192019-04-24 16:12:47 +0200572 subnetsToBePopulated.add(subnet);
psneha86e60d32019-03-26 06:31:41 -0400573 }
piera9941192019-04-24 16:12:47 +0200574 subnets = subnetsToBePopulated;
sangho80f11cb2015-04-01 13:05:26 -0700575 }
piera9941192019-04-24 16:12:47 +0200576 // populate the remaining routes in the target switch
577 return populateIpRulesForRouter(targetSw, subnets, destSw1, destSw2, nextHops);
Charles Chanc22cef32016-04-29 14:38:22 -0700578 }
sangho80f11cb2015-04-01 13:05:26 -0700579
Charles Chanc22cef32016-04-29 14:38:22 -0700580 /**
Charles Chand66d6712018-03-29 16:03:41 -0700581 * Revokes IP flow rules for the subnets from given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700582 *
Charles Chand66d6712018-03-29 16:03:41 -0700583 * @param targetSw target switch from which the subnets need to be removed
Charles Chanc22cef32016-04-29 14:38:22 -0700584 * @param subnets subnet being removed
585 * @return true if all rules are removed successfully, false otherwise
586 */
Charles Chand66d6712018-03-29 16:03:41 -0700587 boolean revokeIpRuleForSubnet(DeviceId targetSw, Set<IpPrefix> subnets) {
Charles Chanc22cef32016-04-29 14:38:22 -0700588 for (IpPrefix subnet : subnets) {
Charles Chand66d6712018-03-29 16:03:41 -0700589 if (!revokeIpRuleForRouter(targetSw, subnet)) {
Charles Chanc22cef32016-04-29 14:38:22 -0700590 return false;
591 }
592 }
sangho80f11cb2015-04-01 13:05:26 -0700593 return true;
594 }
595
596 /**
piera9941192019-04-24 16:12:47 +0200597 * Populates IP flow rules for a set of IP prefix in the target device.
598 * The prefix are reachable via destination device(s).
sangho80f11cb2015-04-01 13:05:26 -0700599 *
Saurav Das261c3002017-06-13 15:35:54 -0700600 * @param targetSw target device ID to set the rules
piera9941192019-04-24 16:12:47 +0200601 * @param subnets the set of IP prefix
Saurav Das261c3002017-06-13 15:35:54 -0700602 * @param destSw1 destination switch where the prefixes are reachable
603 * @param destSw2 paired destination switch if one exists for the subnets/prefixes.
604 * Should be null if there is no paired destination switch (by config)
605 * or if the given prefixes are reachable only via destSw1
606 * @param nextHops map of destination switches and their next-hops.
607 * Should only contain destination switches that are
608 * actually meant to be routed to. If destSw2 is null, there
609 * should not be an entry for destSw2 in this map.
sangho80f11cb2015-04-01 13:05:26 -0700610 * @return true if all rules are set successfully, false otherwise
611 */
piera9941192019-04-24 16:12:47 +0200612 private boolean populateIpRulesForRouter(DeviceId targetSw,
613 Set<IpPrefix> subnets,
614 DeviceId destSw1, DeviceId destSw2,
615 Map<DeviceId, Set<DeviceId>> nextHops) {
616 // pre-compute the needed information
617 int segmentIdIPv41, segmentIdIPv42 = -1;
618 int segmentIdIPv61, segmentIdIPv62 = -1;
619 TrafficTreatment treatment = null;
620 DestinationSet dsIPv4, dsIPv6;
621 TrafficSelector metaIpv4Selector, metaIpv6Selector = null;
622 int nextIdIPv4, nextIdIPv6, nextId;
623 TrafficSelector selector;
624 // start with MPLS SIDs
Charles Chan319d1a22015-11-03 10:42:14 -0800625 try {
piera9941192019-04-24 16:12:47 +0200626 segmentIdIPv41 = config.getIPv4SegmentId(destSw1);
627 segmentIdIPv61 = config.getIPv6SegmentId(destSw1);
628 if (destSw2 != null) {
629 segmentIdIPv42 = config.getIPv4SegmentId(destSw2);
630 segmentIdIPv62 = config.getIPv6SegmentId(destSw2);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800631 }
Charles Chan319d1a22015-11-03 10:42:14 -0800632 } catch (DeviceConfigNotFoundException e) {
633 log.warn(e.getMessage() + " Aborting populateIpRuleForRouter.");
634 return false;
635 }
piera9941192019-04-24 16:12:47 +0200636 // build the IPv4 and IPv6 destination set
Saurav Das261c3002017-06-13 15:35:54 -0700637 if (destSw2 == null) {
638 // single dst - create destination set based on next-hop
Saurav Das97241862018-02-14 14:14:54 -0800639 // If the next hop is the same as the final destination, then MPLS
640 // label is not set.
Saurav Das261c3002017-06-13 15:35:54 -0700641 Set<DeviceId> nhd1 = nextHops.get(destSw1);
642 if (nhd1.size() == 1 && nhd1.iterator().next().equals(destSw1)) {
piera9941192019-04-24 16:12:47 +0200643 dsIPv4 = DestinationSet.createTypePushNone(destSw1);
644 dsIPv6 = DestinationSet.createTypePushNone(destSw1);
645 treatment = DefaultTrafficTreatment.builder()
646 .immediate()
647 .decNwTtl()
648 .build();
Saurav Das261c3002017-06-13 15:35:54 -0700649 } else {
piera9941192019-04-24 16:12:47 +0200650 dsIPv4 = DestinationSet.createTypePushBos(segmentIdIPv41, destSw1);
651 dsIPv6 = DestinationSet.createTypePushBos(segmentIdIPv61, destSw1);
Saurav Das261c3002017-06-13 15:35:54 -0700652 }
653 } else {
654 // dst pair - IP rules for dst-pairs are always from other edge nodes
655 // the destination set needs to have both destinations, even if there
656 // are no next hops to one of them
piera9941192019-04-24 16:12:47 +0200657 dsIPv4 = DestinationSet.createTypePushBos(segmentIdIPv41, destSw1, segmentIdIPv42, destSw2);
658 dsIPv6 = DestinationSet.createTypePushBos(segmentIdIPv61, destSw1, segmentIdIPv62, destSw2);
sangho80f11cb2015-04-01 13:05:26 -0700659 }
660
Saurav Das4c35fc42015-11-20 15:27:53 -0800661 // setup metadata to pass to nextObjective - indicate the vlan on egress
662 // if needed by the switch pipeline. Since neighbor sets are always to
663 // other neighboring routers, there is no subnet assigned on those ports.
piera9941192019-04-24 16:12:47 +0200664 metaIpv4Selector = buildIpv4Selector()
665 .matchVlanId(srManager.getDefaultInternalVlan())
666 .build();
667 metaIpv6Selector = buildIpv6Selector()
668 .matchVlanId(srManager.getDefaultInternalVlan())
669 .build();
670 // get the group handler of the target switch
Saurav Das261c3002017-06-13 15:35:54 -0700671 DefaultGroupHandler grpHandler = srManager.getGroupHandler(targetSw);
Saurav Das62ae6792017-05-15 15:34:25 -0700672 if (grpHandler == null) {
673 log.warn("populateIPRuleForRouter: groupHandler for device {} "
piera9941192019-04-24 16:12:47 +0200674 + "not found", targetSw);
Saurav Das62ae6792017-05-15 15:34:25 -0700675 return false;
676 }
piera9941192019-04-24 16:12:47 +0200677 // get next id
678 nextIdIPv4 = grpHandler.getNextObjectiveId(dsIPv4, nextHops, metaIpv4Selector, false);
679 if (nextIdIPv4 <= 0) {
680 log.warn("No next objective in {} for ds: {}", targetSw, dsIPv4);
sangho2165d222015-05-01 09:38:25 -0700681 return false;
682 }
piera9941192019-04-24 16:12:47 +0200683 nextIdIPv6 = grpHandler.getNextObjectiveId(dsIPv6, nextHops, metaIpv6Selector, false);
684 if (nextIdIPv6 <= 0) {
685 log.warn("No next objective in {} for ds: {}", targetSw, dsIPv6);
686 return false;
Charles Chanf4586112015-11-09 16:37:23 -0800687 }
piera9941192019-04-24 16:12:47 +0200688 // build all the flow rules and send to the device
689 for (IpPrefix subnet : subnets) {
690 selector = buildIpSelectorFromIpPrefix(subnet).build();
691 if (subnet.isIp4()) {
692 nextId = nextIdIPv4;
693 } else {
694 nextId = nextIdIPv6;
695 }
696 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
697 .builder()
698 .fromApp(srManager.appId)
699 .makePermanent()
700 .nextStep(nextId)
701 .withSelector(selector)
702 .withPriority(getPriorityFromPrefix(subnet))
703 .withFlag(ForwardingObjective.Flag.SPECIFIC);
704 if (treatment != null) {
705 fwdBuilder.withTreatment(treatment);
706 }
707 log.debug("Installing {} forwarding objective for router IP/subnet {} "
708 + "in switch {} with nextId: {}", subnet.isIp4() ? "IPv4" : "IPv6",
709 subnet, targetSw, nextId);
710 ObjectiveContext context = new DefaultObjectiveContext(
711 (objective) -> log.debug("IP rule for router {} populated in dev:{}",
712 subnet, targetSw),
713 (objective, error) -> log.warn("Failed to populate IP rule for router {}: {} in dev:{}",
714 subnet, error, targetSw));
715 srManager.flowObjectiveService.forward(targetSw, fwdBuilder.add(context));
716 }
717 rulePopulationCounter.addAndGet(subnets.size());
sangho80f11cb2015-04-01 13:05:26 -0700718 return true;
719 }
720
sangho80f11cb2015-04-01 13:05:26 -0700721 /**
Charles Chand66d6712018-03-29 16:03:41 -0700722 * Revokes IP flow rules for the router IP address from given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700723 *
Charles Chand66d6712018-03-29 16:03:41 -0700724 * @param targetSw target switch from which the ipPrefix need to be removed
Charles Chanc22cef32016-04-29 14:38:22 -0700725 * @param ipPrefix the IP address of the destination router
726 * @return true if all rules are removed successfully, false otherwise
727 */
Charles Chand66d6712018-03-29 16:03:41 -0700728 private boolean revokeIpRuleForRouter(DeviceId targetSw, IpPrefix ipPrefix) {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800729 TrafficSelector.Builder sbuilder = buildIpSelectorFromIpPrefix(ipPrefix);
Charles Chanc22cef32016-04-29 14:38:22 -0700730 TrafficSelector selector = sbuilder.build();
731 TrafficTreatment dummyTreatment = DefaultTrafficTreatment.builder().build();
732
733 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
734 .builder()
735 .fromApp(srManager.appId)
736 .makePermanent()
737 .withSelector(selector)
738 .withTreatment(dummyTreatment)
739 .withPriority(getPriorityFromPrefix(ipPrefix))
740 .withFlag(ForwardingObjective.Flag.SPECIFIC);
741
Charles Chand66d6712018-03-29 16:03:41 -0700742 ObjectiveContext context = new DefaultObjectiveContext(
743 (objective) -> log.debug("IP rule for router {} revoked from {}", ipPrefix, targetSw),
744 (objective, error) -> log.warn("Failed to revoke IP rule for router {} from {}: {}",
745 ipPrefix, targetSw, error));
746 srManager.flowObjectiveService.forward(targetSw, fwdBuilder.remove(context));
Charles Chanc22cef32016-04-29 14:38:22 -0700747
748 return true;
749 }
750
751 /**
Saurav Das97241862018-02-14 14:14:54 -0800752 * Populates MPLS flow rules in the target device to point towards the
753 * destination device.
754 *
755 * @param targetSwId target device ID of the switch to set the rules
756 * @param destSwId destination switch device ID
757 * @param nextHops next hops switch ID list
758 * @param routerIp the router ip of the destination switch
759 * @return true if all rules are set successfully, false otherwise
760 */
761 boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
762 Set<DeviceId> nextHops, IpAddress routerIp) {
763 int segmentId;
764 try {
765 if (routerIp.isIp4()) {
766 segmentId = config.getIPv4SegmentId(destSwId);
767 } else {
768 segmentId = config.getIPv6SegmentId(destSwId);
769 }
770 } catch (DeviceConfigNotFoundException e) {
771 log.warn(e.getMessage() + " Aborting populateMplsRule.");
772 return false;
773 }
774
775 List<ForwardingObjective> fwdObjs = new ArrayList<>();
776 Collection<ForwardingObjective> fwdObjsMpls;
777 // Generates the transit rules used by the standard "routing".
778 fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId,
779 routerIp, true);
780 if (fwdObjsMpls.isEmpty()) {
781 return false;
782 }
783 fwdObjs.addAll(fwdObjsMpls);
784
785 // Generates the transit rules used by the MPLS Pwaas.
786 int pwSrLabel;
787 try {
788 pwSrLabel = config.getPWRoutingLabel(destSwId);
789 } catch (DeviceConfigNotFoundException e) {
790 log.warn(e.getMessage()
791 + " Aborting populateMplsRule. No label for PseudoWire traffic.");
792 return false;
793 }
794 fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, pwSrLabel,
795 routerIp, false);
796 if (fwdObjsMpls.isEmpty()) {
797 return false;
798 }
799 fwdObjs.addAll(fwdObjsMpls);
800
801 for (ForwardingObjective fwdObj : fwdObjs) {
802 log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
803 fwdObj.id(), segmentId, fwdObj.nextId(), targetSwId);
804 srManager.flowObjectiveService.forward(targetSwId, fwdObj);
805 rulePopulationCounter.incrementAndGet();
806 }
807
808 return true;
809 }
810
811 /**
812 * Differentiates between popping and swapping labels when building an MPLS
813 * forwarding objective.
Pier Ventre229fd0b2016-10-31 16:49:19 -0700814 *
815 * @param targetSwId the target sw
816 * @param destSwId the destination sw
817 * @param nextHops the set of next hops
Saurav Das97241862018-02-14 14:14:54 -0800818 * @param segmentId the segmentId to match representing the destination
819 * switch
820 * @param routerIp the router ip representing the destination switch
Pier Ventre229fd0b2016-10-31 16:49:19 -0700821 * @return a collection of fwdobjective
822 */
pierventree9261c92021-08-27 13:12:06 +0200823 private Collection<ForwardingObjective> handleMpls(DeviceId targetSwId, DeviceId destSwId,
824 Set<DeviceId> nextHops, int segmentId, IpAddress routerIp,
Saurav Das261c3002017-06-13 15:35:54 -0700825 boolean isMplsBos) {
Pier Ventre229fd0b2016-10-31 16:49:19 -0700826
827 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
828 List<ForwardingObjective.Builder> fwdObjBuilders = Lists.newArrayList();
Pier Luigi0be3f0f2017-01-30 09:47:36 -0800829 // For the transport of Pwaas we can use two or three MPLS label
Pier Ventre229fd0b2016-10-31 16:49:19 -0700830 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
831 sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
832 sbuilder.matchMplsBos(isMplsBos);
833 TrafficSelector selector = sbuilder.build();
834
835 // setup metadata to pass to nextObjective - indicate the vlan on egress
836 // if needed by the switch pipeline. Since mpls next-hops are always to
837 // other neighboring routers, there is no subnet assigned on those ports.
838 TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
Saurav Das9bf49582018-08-13 15:34:26 -0700839 metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
Pier Ventre229fd0b2016-10-31 16:49:19 -0700840
841 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
842 // If the next hop is the destination router for the segment, do pop
843 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
Saurav Das261c3002017-06-13 15:35:54 -0700844 + "label {} in switch {} with pop to next-hops {}",
845 segmentId, targetSwId, nextHops);
Saurav Das2cb38292017-03-29 19:09:17 -0700846 ForwardingObjective.Builder fwdObjNoBosBuilder =
pierventree9261c92021-08-27 13:12:06 +0200847 getMplsForwardingObjective(targetSwId, nextHops, true, isMplsBos,
848 metabuilder.build(), routerIp, segmentId, destSwId);
Pier Ventre229fd0b2016-10-31 16:49:19 -0700849 // Error case, we cannot handle, exit.
850 if (fwdObjNoBosBuilder == null) {
851 return Collections.emptyList();
852 }
853 fwdObjBuilders.add(fwdObjNoBosBuilder);
Pier Ventre229fd0b2016-10-31 16:49:19 -0700854 } else {
Saurav Das97241862018-02-14 14:14:54 -0800855 // next hop is not destination, irrespective of the number of next
856 // hops (1 or more) -- SR CONTINUE case (swap with self)
Saurav Das261c3002017-06-13 15:35:54 -0700857 log.debug("Installing MPLS forwarding objective for "
858 + "label {} in switch {} without pop to next-hops {}",
859 segmentId, targetSwId, nextHops);
Saurav Das2cb38292017-03-29 19:09:17 -0700860 ForwardingObjective.Builder fwdObjNoBosBuilder =
pierventree9261c92021-08-27 13:12:06 +0200861 getMplsForwardingObjective(targetSwId, nextHops, false, isMplsBos,
862 metabuilder.build(), routerIp, segmentId, destSwId);
Pier Ventre229fd0b2016-10-31 16:49:19 -0700863 // Error case, we cannot handle, exit.
864 if (fwdObjNoBosBuilder == null) {
865 return Collections.emptyList();
866 }
867 fwdObjBuilders.add(fwdObjNoBosBuilder);
Pier Ventre229fd0b2016-10-31 16:49:19 -0700868 }
869
870 List<ForwardingObjective> fwdObjs = Lists.newArrayList();
871 // We add the final property to the fwdObjs.
872 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
Pier Ventre229fd0b2016-10-31 16:49:19 -0700873 ((Builder) ((Builder) fwdObjBuilder
874 .fromApp(srManager.appId)
875 .makePermanent())
876 .withSelector(selector)
877 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
878 .withFlag(ForwardingObjective.Flag.SPECIFIC);
879
880 ObjectiveContext context = new DefaultObjectiveContext(
pierventree9261c92021-08-27 13:12:06 +0200881 (objective) -> log.debug("MPLS rule {} for SID {} populated in dev:{} ",
Pier Ventre229fd0b2016-10-31 16:49:19 -0700882 objective.id(), segmentId, targetSwId),
pierventree9261c92021-08-27 13:12:06 +0200883 (objective, error) -> log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
Pier Ventre229fd0b2016-10-31 16:49:19 -0700884 objective.id(), segmentId, error, targetSwId));
885
886 ForwardingObjective fob = fwdObjBuilder.add(context);
887 fwdObjs.add(fob);
Pier Ventre229fd0b2016-10-31 16:49:19 -0700888 }
889
890 return fwdObjs;
891 }
892
893 /**
Saurav Das97241862018-02-14 14:14:54 -0800894 * Returns a Forwarding Objective builder for the MPLS rule that references
895 * the desired Next Objective. Creates a DestinationSet that allows the
896 * groupHandler to create or find the required next objective.
sangho80f11cb2015-04-01 13:05:26 -0700897 *
Saurav Das97241862018-02-14 14:14:54 -0800898 * @param targetSw the target sw
899 * @param nextHops the set of next hops
900 * @param phpRequired true if penultimate-hop-popping is required
901 * @param isBos true if matched label is bottom-of-stack
902 * @param meta metadata for creating next objective
903 * @param routerIp the router ip representing the destination switch
904 * @param destSw the destination sw
905 * @return the mpls forwarding objective builder
sangho80f11cb2015-04-01 13:05:26 -0700906 */
pierventree9261c92021-08-27 13:12:06 +0200907 private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId targetSw, Set<DeviceId> nextHops,
908 boolean phpRequired, boolean isBos, TrafficSelector meta,
909 IpAddress routerIp, int segmentId, DeviceId destSw) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800910
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700911 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
912 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
sangho80f11cb2015-04-01 13:05:26 -0700913
914 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Saurav Das97241862018-02-14 14:14:54 -0800915 DestinationSet ds = null;
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700916 DestinationSet.DestinationSetType dstType = null;
Saurav Das97241862018-02-14 14:14:54 -0800917 boolean simple = false;
sangho80f11cb2015-04-01 13:05:26 -0700918 if (phpRequired) {
Saurav Das4c35fc42015-11-20 15:27:53 -0800919 // php case - pop should always be flow-action
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700920 log.debug("getMplsForwardingObjective: php required");
sangho27462c62015-05-14 00:39:53 -0700921 tbuilder.deferred().copyTtlIn();
sangho80f11cb2015-04-01 13:05:26 -0700922 if (isBos) {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800923 if (routerIp.isIp4()) {
924 tbuilder.deferred().popMpls(EthType.EtherType.IPV4.ethType());
925 } else {
926 tbuilder.deferred().popMpls(EthType.EtherType.IPV6.ethType());
927 }
928 tbuilder.decNwTtl();
Saurav Das97241862018-02-14 14:14:54 -0800929 // standard case -> BoS == True; pop results in IP packet and forwarding
930 // is via an ECMP group
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700931 ds = DestinationSet.createTypePopBos(destSw);
sangho80f11cb2015-04-01 13:05:26 -0700932 } else {
Saurav Das4c35fc42015-11-20 15:27:53 -0800933 tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
934 .decMplsTtl();
Saurav Das97241862018-02-14 14:14:54 -0800935 // double-label case -> BoS == False, pop results in MPLS packet
936 // depending on configuration we can ECMP this packet or choose one output
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700937 ds = DestinationSet.createTypePopNotBos(destSw);
938 if (!srManager.getMplsEcmp()) {
939 simple = true;
Saurav Das97241862018-02-14 14:14:54 -0800940 }
sangho80f11cb2015-04-01 13:05:26 -0700941 }
942 } else {
Saurav Das4c35fc42015-11-20 15:27:53 -0800943 // swap with self case - SR CONTINUE
Saurav Das97241862018-02-14 14:14:54 -0800944 log.debug("getMplsForwardingObjective: swap with self");
sangho27462c62015-05-14 00:39:53 -0700945 tbuilder.deferred().decMplsTtl();
Saurav Das97241862018-02-14 14:14:54 -0800946 // swap results in MPLS packet with same BoS bit regardless of bit value
947 // depending on configuration we can ECMP this packet or choose one output
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -0700948 // differentiate here between swap with not bos or swap with bos
949 ds = isBos ? DestinationSet.createTypeSwapBos(segmentId, destSw) :
950 DestinationSet.createTypeSwapNotBos(segmentId, destSw);
951 if (!srManager.getMplsEcmp()) {
Saurav Das97241862018-02-14 14:14:54 -0800952 simple = true;
953 }
sangho80f11cb2015-04-01 13:05:26 -0700954 }
955
Saurav Das4c35fc42015-11-20 15:27:53 -0800956 fwdBuilder.withTreatment(tbuilder.build());
Saurav Das97241862018-02-14 14:14:54 -0800957 log.debug("Trying to get a nextObjId for mpls rule on device:{} to ds:{}",
958 targetSw, ds);
Saurav Das261c3002017-06-13 15:35:54 -0700959 DefaultGroupHandler gh = srManager.getGroupHandler(targetSw);
960 if (gh == null) {
961 log.warn("getNextObjectiveId query - groupHandler for device {} "
962 + "not found", targetSw);
963 return null;
964 }
Saurav Das97241862018-02-14 14:14:54 -0800965
Saurav Das261c3002017-06-13 15:35:54 -0700966 Map<DeviceId, Set<DeviceId>> dstNextHops = new HashMap<>();
967 dstNextHops.put(destSw, nextHops);
Saurav Das97241862018-02-14 14:14:54 -0800968 int nextId = gh.getNextObjectiveId(ds, dstNextHops, meta, simple);
Saurav Das4c35fc42015-11-20 15:27:53 -0800969 if (nextId <= 0) {
Saurav Das97241862018-02-14 14:14:54 -0800970 log.warn("No next objective in {} for ds: {}", targetSw, ds);
Saurav Das4c35fc42015-11-20 15:27:53 -0800971 return null;
Saurav Dase0237a32016-05-27 13:54:07 -0700972 } else {
Saurav Das97241862018-02-14 14:14:54 -0800973 log.debug("nextObjId found:{} for mpls rule on device:{} to ds:{}",
pierventree9261c92021-08-27 13:12:06 +0200974 nextId, targetSw, ds);
sangho80f11cb2015-04-01 13:05:26 -0700975 }
976
Saurav Das4c35fc42015-11-20 15:27:53 -0800977 fwdBuilder.nextStep(nextId);
Srikanth Vavilapalli64505482015-04-21 13:04:13 -0700978 return fwdBuilder;
sangho80f11cb2015-04-01 13:05:26 -0700979 }
980
981 /**
Saurav Das9f1c42e2015-10-23 10:51:11 -0700982 * Creates a filtering objective to permit all untagged packets with a
Saurav Das7c305372015-10-28 12:39:42 -0700983 * dstMac corresponding to the router's MAC address. For those pipelines
984 * that need to internally assign vlans to untagged packets, this method
985 * provides per-subnet vlan-ids as metadata.
Saurav Dasc28b3432015-10-30 17:45:38 -0700986 * <p>
Saurav Dasf9332192017-02-18 14:05:44 -0800987 * Note that the vlan assignment and filter programming should only be done by
988 * the master for a switch. This method is typically called at deviceAdd and
989 * programs filters only for the enabled ports of the device. For port-updates,
990 * that enable/disable ports after device add, singlePortFilter methods should
991 * be called.
sangho80f11cb2015-04-01 13:05:26 -0700992 *
Saurav Das9f1c42e2015-10-23 10:51:11 -0700993 * @param deviceId the switch dpid for the router
Saurav Dasd1872b02016-12-02 15:43:47 -0800994 * @return PortFilterInfo information about the processed ports
sangho80f11cb2015-04-01 13:05:26 -0700995 */
Charles Chan3ed34d82017-06-22 18:03:14 -0700996 PortFilterInfo populateVlanMacFilters(DeviceId deviceId) {
Saurav Das7c305372015-10-28 12:39:42 -0700997 log.debug("Installing per-port filtering objective for untagged "
998 + "packets in device {}", deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800999
Saurav Das07c74602016-04-27 18:35:50 -07001000 List<Port> devPorts = srManager.deviceService.getPorts(deviceId);
Jon Hall31d84782017-01-18 20:15:44 -08001001 if (devPorts == null || devPorts.isEmpty()) {
Saurav Das07c74602016-04-27 18:35:50 -07001002 log.warn("Device {} ports not available. Unable to add MacVlan filters",
1003 deviceId);
Saurav Dasd1872b02016-12-02 15:43:47 -08001004 return null;
Saurav Das07c74602016-04-27 18:35:50 -07001005 }
Saurav Dasf9332192017-02-18 14:05:44 -08001006 int disabledPorts = 0, errorPorts = 0, filteredPorts = 0;
Saurav Das07c74602016-04-27 18:35:50 -07001007 for (Port port : devPorts) {
Saurav Dase0237a32016-05-27 13:54:07 -07001008 if (!port.isEnabled()) {
1009 disabledPorts++;
1010 continue;
1011 }
Charles Chan43be46b2017-02-26 22:59:35 -08001012 if (processSinglePortFilters(deviceId, port.number(), true)) {
Saurav Dasf9332192017-02-18 14:05:44 -08001013 filteredPorts++;
1014 } else {
1015 errorPorts++;
Saurav Dase0237a32016-05-27 13:54:07 -07001016 }
Saurav Das7c305372015-10-28 12:39:42 -07001017 }
Charles Chan077314e2017-06-22 14:27:17 -07001018 log.debug("Filtering on dev:{}, disabledPorts:{}, errorPorts:{}, filteredPorts:{}",
Saurav Dasf9332192017-02-18 14:05:44 -08001019 deviceId, disabledPorts, errorPorts, filteredPorts);
Charles Chan9d2dd552018-06-19 20:56:33 -07001020 return new PortFilterInfo(disabledPorts, errorPorts, filteredPorts);
Saurav Dasf9332192017-02-18 14:05:44 -08001021 }
1022
1023 /**
Charles Chan43be46b2017-02-26 22:59:35 -08001024 * Creates or removes filtering objectives for a single port. Should only be
1025 * called by the master for a switch.
Saurav Dasf9332192017-02-18 14:05:44 -08001026 *
1027 * @param deviceId device identifier
1028 * @param portnum port identifier for port to be filtered
Charles Chan43be46b2017-02-26 22:59:35 -08001029 * @param install true to install the filtering objective, false to remove
Saurav Dasf9332192017-02-18 14:05:44 -08001030 * @return true if no errors occurred during the build of the filtering objective
1031 */
Charles Chan3ed34d82017-06-22 18:03:14 -07001032 boolean processSinglePortFilters(DeviceId deviceId, PortNumber portnum, boolean install) {
Charles Chan90772a72017-02-08 15:52:08 -08001033 ConnectPoint connectPoint = new ConnectPoint(deviceId, portnum);
Charles Chan098ca202018-05-01 11:50:20 -07001034 VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
1035 Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
1036 VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
Charles Chan90772a72017-02-08 15:52:08 -08001037
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001038 // Do not configure filter for edge ports where double-tagged hosts are connected.
Charles Chan90772a72017-02-08 15:52:08 -08001039 if (taggedVlans.size() != 0) {
1040 // Filter for tagged vlans
Charles Chan098ca202018-05-01 11:50:20 -07001041 if (!srManager.interfaceService.getTaggedVlanId(connectPoint).stream().allMatch(taggedVlanId ->
pierventrea3989be2021-01-08 16:43:17 +01001042 processSinglePortFiltersInternal(deviceId, portnum, false, taggedVlanId,
1043 install, false))) {
Charles Chan90772a72017-02-08 15:52:08 -08001044 return false;
1045 }
1046 if (nativeVlan != null) {
1047 // Filter for native vlan
pierventrea3989be2021-01-08 16:43:17 +01001048 if (!processSinglePortFiltersInternal(deviceId, portnum, true, nativeVlan,
1049 install, false)) {
Charles Chan90772a72017-02-08 15:52:08 -08001050 return false;
1051 }
1052 }
1053 } else if (untaggedVlan != null) {
1054 // Filter for untagged vlan
pierventrea3989be2021-01-08 16:43:17 +01001055 if (!processSinglePortFiltersInternal(deviceId, portnum, true, untaggedVlan,
1056 install, false)) {
Charles Chan90772a72017-02-08 15:52:08 -08001057 return false;
1058 }
Jonghwan Hyun671a7ab2018-04-30 09:27:21 -07001059 } else if (!hasIPConfiguration(connectPoint)) {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001060 // Filter for unconfigured upstream port, using INTERNAL_VLAN
Saurav Das9bf49582018-08-13 15:34:26 -07001061 if (!processSinglePortFiltersInternal(deviceId, portnum, true,
1062 srManager.getDefaultInternalVlan(),
pierventrea3989be2021-01-08 16:43:17 +01001063 install, false)) {
Charles Chan90772a72017-02-08 15:52:08 -08001064 return false;
1065 }
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -07001066 // Filter for receiveing pseudowire traffic
Saurav Das9bf49582018-08-13 15:34:26 -07001067 if (!processSinglePortFiltersInternal(deviceId, portnum, false,
1068 srManager.getPwTransportVlan(),
pierventrea3989be2021-01-08 16:43:17 +01001069 install, false)) {
Andreas Pantelopoulosb281ae22018-05-01 14:56:05 -07001070 return false;
1071 }
Charles Chan90772a72017-02-08 15:52:08 -08001072 }
1073 return true;
1074 }
1075
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001076 /**
1077 * Updates filtering objectives for a single port. Should only be called by
1078 * the master for a switch
1079 * @param deviceId device identifier
1080 * @param portNum port identifier for port to be filtered
1081 * @param pushVlan true to push vlan, false otherwise
1082 * @param vlanId vlan identifier
1083 * @param install true to install the filtering objective, false to remove
1084 */
1085 void updateSinglePortFilters(DeviceId deviceId, PortNumber portNum,
1086 boolean pushVlan, VlanId vlanId, boolean install) {
pierventrea3989be2021-01-08 16:43:17 +01001087 // NOTE: update port filters is used only when the vlan configuration
1088 // has been updated. Port configuration is never removed here.
1089 // For pipeline sharing the TMAC entry among different vlans
1090 // there is no need to push or remove the TMAC entry
1091 if (!processSinglePortFiltersInternal(deviceId, portNum, pushVlan, vlanId, install, true)) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001092 log.warn("Failed to update FilteringObjective for {}/{} with vlan {}",
1093 deviceId, portNum, vlanId);
1094 }
1095 }
1096
Charles Chan43be46b2017-02-26 22:59:35 -08001097 private boolean processSinglePortFiltersInternal(DeviceId deviceId, PortNumber portnum,
pierventrea3989be2021-01-08 16:43:17 +01001098 boolean pushVlan, VlanId vlanId, boolean install,
1099 boolean update) {
Daniel Ginsburg776789f2018-04-30 19:27:19 -04001100 boolean doTMAC = true;
1101
1102 if (!pushVlan) {
1103 // Skip the tagged vlans belonging to an interface without an IP address
1104 Set<Interface> ifaces = srManager.interfaceService
1105 .getInterfacesByPort(new ConnectPoint(deviceId, portnum))
1106 .stream()
1107 .filter(intf -> intf.vlanTagged().contains(vlanId) && intf.ipAddressesList().isEmpty())
1108 .collect(Collectors.toSet());
1109 if (!ifaces.isEmpty()) {
1110 log.debug("processSinglePortFiltersInternal: skipping TMAC for vlan {} at {}/{} - no IP",
1111 vlanId, deviceId, portnum);
1112 doTMAC = false;
1113 }
1114 }
1115
pierventrea3989be2021-01-08 16:43:17 +01001116 FilteringObjective.Builder fob = buildFilteringObjective(deviceId, portnum, pushVlan, vlanId, doTMAC, update);
Saurav Dasf9332192017-02-18 14:05:44 -08001117 if (fob == null) {
1118 // error encountered during build
1119 return false;
1120 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001121 log.debug("{} filtering objectives for dev/port: {}/{}",
Saurav Das62ae6792017-05-15 15:34:25 -07001122 install ? "Installing" : "Removing", deviceId, portnum);
Saurav Dasf9332192017-02-18 14:05:44 -08001123 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan43be46b2017-02-26 22:59:35 -08001124 (objective) -> log.debug("Filter for {}/{} {}", deviceId, portnum,
Saurav Das62ae6792017-05-15 15:34:25 -07001125 install ? "installed" : "removed"),
Charles Chan43be46b2017-02-26 22:59:35 -08001126 (objective, error) -> log.warn("Failed to {} filter for {}/{}: {}",
Saurav Das62ae6792017-05-15 15:34:25 -07001127 install ? "install" : "remove", deviceId, portnum, error));
Charles Chan43be46b2017-02-26 22:59:35 -08001128 if (install) {
1129 srManager.flowObjectiveService.filter(deviceId, fob.add(context));
1130 } else {
1131 srManager.flowObjectiveService.filter(deviceId, fob.remove(context));
Charles Chan90772a72017-02-08 15:52:08 -08001132 }
Charles Chan90772a72017-02-08 15:52:08 -08001133 return true;
Saurav Dasf9332192017-02-18 14:05:44 -08001134 }
1135
Charles Chan90772a72017-02-08 15:52:08 -08001136 private FilteringObjective.Builder buildFilteringObjective(DeviceId deviceId, PortNumber portnum,
pierventrea3989be2021-01-08 16:43:17 +01001137 boolean pushVlan, VlanId vlanId,
1138 boolean doTMAC, boolean update) {
Saurav Dasf9332192017-02-18 14:05:44 -08001139 MacAddress deviceMac;
pierventree9261c92021-08-27 13:12:06 +02001140 long metadata = 0;
1141 boolean isPairPort;
Saurav Dasf9332192017-02-18 14:05:44 -08001142 try {
1143 deviceMac = config.getDeviceMac(deviceId);
pierventree9261c92021-08-27 13:12:06 +02001144 isPairPort = portnum.equals(config.getPairLocalPort(deviceId));
Saurav Dasf9332192017-02-18 14:05:44 -08001145 } catch (DeviceConfigNotFoundException e) {
1146 log.warn(e.getMessage() + " Processing SinglePortFilters aborted");
1147 return null;
1148 }
Saurav Dasf9332192017-02-18 14:05:44 -08001149 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
Daniel Ginsburg776789f2018-04-30 19:27:19 -04001150
1151 if (doTMAC) {
1152 fob.withKey(Criteria.matchInPort(portnum))
1153 .addCondition(Criteria.matchEthDst(deviceMac))
1154 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
1155 } else {
1156 fob.withKey(Criteria.matchInPort(portnum))
1157 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
1158 }
Charles Chan90772a72017-02-08 15:52:08 -08001159
Charles Chan17ca2202017-12-19 19:55:57 -08001160 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1161
Charles Chan90772a72017-02-08 15:52:08 -08001162 if (pushVlan) {
1163 fob.addCondition(Criteria.matchVlanId(VlanId.NONE));
Charles Chan17ca2202017-12-19 19:55:57 -08001164 tBuilder.pushVlan().setVlanId(vlanId);
Charles Chan90772a72017-02-08 15:52:08 -08001165 } else {
1166 fob.addCondition(Criteria.matchVlanId(vlanId));
1167 }
1168
Charles Chan17ca2202017-12-19 19:55:57 -08001169 // NOTE: Some switch hardware share the same filtering flow among different ports.
1170 // We use this metadata to let the driver know that there is no more enabled port
1171 // within the same VLAN on this device.
Charles Chanf17f66b2018-02-26 21:33:25 -08001172 if (noMoreEnabledPort(deviceId, vlanId)) {
Charles Chan17ca2202017-12-19 19:55:57 -08001173 tBuilder.wipeDeferred();
1174 }
pierventrea3989be2021-01-08 16:43:17 +01001175 // NOTE: Some switch hardware share the same tmac flow among different vlans.
1176 // We use this metadata to let the driver know that there is still a vlan
1177 // configuration associated to that port
1178 if (update) {
pierventree9261c92021-08-27 13:12:06 +02001179 metadata = metadata | INTERFACE_CONFIG_UPDATE;
1180 }
1181 // NOTE: Metadata to signal the driver the port type
1182 metadata = metadata | portType(VlanId.NONE, vlanId, isPairPort);
1183 // NOTE: metadata equals 0 is rejected by the driver
1184 if (metadata > 0) {
1185 tBuilder.writeMetadata(metadata, METADATA_MASK);
pierventrea3989be2021-01-08 16:43:17 +01001186 }
1187
Charles Chan17ca2202017-12-19 19:55:57 -08001188 fob.withMeta(tBuilder.build());
Saurav Dasf9332192017-02-18 14:05:44 -08001189 fob.permit().fromApp(srManager.appId);
1190 return fob;
sangho80f11cb2015-04-01 13:05:26 -07001191 }
1192
1193 /**
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001194 * Creates or removes filtering objectives for a double-tagged host on a port.
1195 *
1196 * @param deviceId device identifier
1197 * @param portNum port identifier for port to be filtered
1198 * @param outerVlan outer VLAN ID
1199 * @param innerVlan inner VLAN ID
1200 * @param install true to install the filtering objective, false to remove
1201 */
1202 void processDoubleTaggedFilter(DeviceId deviceId, PortNumber portNum, VlanId outerVlan,
1203 VlanId innerVlan, boolean install) {
Daniele Moro8fc37b42019-10-29 18:48:35 -07001204 // We should trigger the removal of double tagged rules only when removing
1205 // the filtering objective and no other hosts are connected to the same device port.
Charles Chanf17fade2020-03-08 18:07:19 -07001206 boolean cleanupDoubleTaggedRules = !anyDoubleTaggedHost(deviceId, portNum) && !install;
pierventree9261c92021-08-27 13:12:06 +02001207 FilteringObjective.Builder fob = buildDoubleTaggedFilteringObj(deviceId, portNum, outerVlan,
1208 innerVlan, cleanupDoubleTaggedRules);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001209 if (fob == null) {
1210 // error encountered during build
1211 return;
1212 }
1213 log.debug("{} double-tagged filtering objectives for dev/port: {}/{}",
1214 install ? "Installing" : "Removing", deviceId, portNum);
1215 ObjectiveContext context = new DefaultObjectiveContext(
1216 (objective) -> log.debug("Filter for {}/{} {}", deviceId, portNum,
1217 install ? "installed" : "removed"),
1218 (objective, error) -> log.warn("Failed to {} filter for {}/{}: {}",
1219 install ? "install" : "remove", deviceId, portNum, error));
1220 if (install) {
1221 srManager.flowObjectiveService.filter(deviceId, fob.add(context));
1222 } else {
1223 srManager.flowObjectiveService.filter(deviceId, fob.remove(context));
1224 }
1225 }
1226
Charles Chanf17fade2020-03-08 18:07:19 -07001227 /**
1228 * Checks if there is any double tagged host attached to given location.
1229 * This method will match on the effective location of a host.
1230 * That is, it will match on auxLocations when auxLocations is not null. Otherwise, it will match on locations.
1231 *
1232 * @param deviceId device ID
1233 * @param portNum port number
1234 * @return true if there is any host attached to given location.
1235 */
1236 private boolean anyDoubleTaggedHost(DeviceId deviceId, PortNumber portNum) {
1237 ConnectPoint cp = new ConnectPoint(deviceId, portNum);
1238 Set<Host> connectedHosts = srManager.hostService.getConnectedHosts(cp, false);
1239 Set<Host> auxConnectedHosts = srManager.hostService.getConnectedHosts(cp, true);
1240 return !auxConnectedHosts.isEmpty() ||
1241 connectedHosts.stream().anyMatch(host -> host.auxLocations() == null);
1242 }
1243
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001244 private FilteringObjective.Builder buildDoubleTaggedFilteringObj(DeviceId deviceId, PortNumber portNum,
Andreas Pantelopoulos59bd97e2018-06-28 17:06:14 -07001245 VlanId outerVlan, VlanId innerVlan,
1246 boolean cleanupDoubleTaggedRules) {
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001247 MacAddress deviceMac;
pierventree9261c92021-08-27 13:12:06 +02001248 long metadata = 0;
1249 boolean isPairPort;
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001250 try {
1251 deviceMac = config.getDeviceMac(deviceId);
pierventree9261c92021-08-27 13:12:06 +02001252 isPairPort = portNum.equals(config.getPairLocalPort(deviceId));
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001253 } catch (DeviceConfigNotFoundException e) {
1254 log.warn(e.getMessage() + " Processing DoubleTaggedFilters aborted");
1255 return null;
1256 }
1257 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
1258 // Outer vlan id match should be appeared before inner vlan id match.
1259 fob.withKey(Criteria.matchInPort(portNum))
1260 .addCondition(Criteria.matchEthDst(deviceMac))
1261 .addCondition(Criteria.matchVlanId(outerVlan))
Daniele Moro998f2df2019-07-12 17:58:54 -07001262 .addCondition(Criteria.matchInnerVlanId(innerVlan))
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001263 .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
1264
1265 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1266 // Pop outer vlan
1267 tBuilder.popVlan();
1268
pierventree9261c92021-08-27 13:12:06 +02001269 // NOTE: Special metadata for driver to signal when clean up double tagged entries
Andreas Pantelopoulos59bd97e2018-06-28 17:06:14 -07001270 if (cleanupDoubleTaggedRules) {
pierventree9261c92021-08-27 13:12:06 +02001271 metadata = metadata | CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES;
Andreas Pantelopoulos59bd97e2018-06-28 17:06:14 -07001272 }
pierventree9261c92021-08-27 13:12:06 +02001273 // NOTE: Metadata to signal the port type to the driver
1274 metadata = metadata | portType(innerVlan, outerVlan, isPairPort);
1275 // NOTE: metadata equals 0 is rejected by the driver
1276 if (metadata > 0) {
1277 tBuilder.writeMetadata(metadata, METADATA_MASK);
1278 }
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001279 // NOTE: Some switch hardware share the same filtering flow among different ports.
1280 // We use this metadata to let the driver know that there is no more enabled port
1281 // within the same VLAN on this device.
1282 if (noMoreEnabledPort(deviceId, outerVlan)) {
1283 tBuilder.wipeDeferred();
1284 }
1285
1286 fob.withMeta(tBuilder.build());
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001287 fob.permit().fromApp(srManager.appId);
1288 return fob;
1289 }
1290
pierventree9261c92021-08-27 13:12:06 +02001291 // Returns port type based on the input parameters
1292 private long portType(VlanId innerVlanId, VlanId outerVlanId, boolean isPairPort) {
1293 if (isPairPort) {
1294 return PAIR_PORT;
1295 }
1296
1297 // Look at the vlan config to determine if it is an INFRA or an EDGE port
1298 boolean outerVlanValid = outerVlanId != null && !outerVlanId.equals(VlanId.NONE);
1299 boolean innerVlanValid = innerVlanId != null && !innerVlanId.equals(VlanId.NONE);
1300
1301 // double tagged interfaces are always edge ports. Edge ports do not match on the
1302 // transport vlans (default internal vlan and pw transport vlan)
1303 if ((innerVlanValid && outerVlanValid) ||
1304 (outerVlanValid && !outerVlanId.equals(srManager.getDefaultInternalVlan()) &&
1305 !outerVlanId.equals(srManager.getPwTransportVlan()))) {
1306 return EDGE_PORT;
1307 }
1308
1309 return INFRA_PORT;
1310 }
1311
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001312 /**
Danny Chang44a7b952020-09-08 15:27:04 -07001313 * Creates packet requests to punt all IP packets for the router.
1314 * @param deviceId the switch dpid for the router
1315 */
1316 void populateIpPunts(DeviceId deviceId) {
1317 manageIpPunts(deviceId, true);
1318 }
1319
1320 /**
1321 * Creates a packet request to punt all IP packets, destined to the
1322 * specified IP address, which should be router's port IP address.
1323 *
1324 * @param deviceId the switch dpid for the router
1325 * @param ipAddress the IP address of the router's port
1326 */
1327 void populateSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
1328 manageSingleIpPunts(deviceId, ipAddress, true);
1329 }
1330
1331 /**
1332 * Revokes packet requests for all devices.
1333 */
1334 void revokePacketsPunts() {
1335 srManager.deviceService.getDevices().forEach(device -> {
1336 manageIpPunts(device.id(), false);
1337 });
1338 }
1339
1340 /**
1341 * Revokes the packet request to punt all IP packets, destined to the
1342 * router's port IP address, except for the router's IP address.
1343 *
1344 * @param deviceId the switch dpid for the router
1345 * @param ipAddress the IP address of the router's port
1346 */
1347 void revokeSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
1348 try {
1349 if (!ipAddress.equals(config.getRouterIpv4(deviceId)) &&
1350 !ipAddress.equals(config.getRouterIpv6(deviceId))) {
1351 manageSingleIpPunts(deviceId, ipAddress, false);
1352 }
1353 } catch (DeviceConfigNotFoundException e) {
1354 log.warn(e.getMessage() + " Aborting revokeSingleIpPunts");
1355 }
1356 }
1357
1358 /**
1359 * Creates or removes forwarding objectives ( packet-requests ) to punt all IP packets, destined to the
Saurav Dasc28b3432015-10-30 17:45:38 -07001360 * router's port IP addresses, to the controller. Note that the input
Saurav Das9f1c42e2015-10-23 10:51:11 -07001361 * port should not be matched on, as these packets can come from any input.
Saurav Dasc28b3432015-10-30 17:45:38 -07001362 * Furthermore, these are applied only by the master instance.
sangho80f11cb2015-04-01 13:05:26 -07001363 *
Saurav Das9f1c42e2015-10-23 10:51:11 -07001364 * @param deviceId the switch dpid for the router
Danny Chang44a7b952020-09-08 15:27:04 -07001365 * @param request true to create a packet request, false to remove
sangho80f11cb2015-04-01 13:05:26 -07001366 */
Danny Chang44a7b952020-09-08 15:27:04 -07001367 void manageIpPunts(DeviceId deviceId, boolean request) {
Saurav Das261c3002017-06-13 15:35:54 -07001368 Ip4Address routerIpv4, pairRouterIpv4 = null;
Charles Chanef8d12e2017-12-05 21:07:38 -08001369 Ip6Address routerIpv6, routerLinkLocalIpv6, pairRouterIpv6 = null;
Charles Chan319d1a22015-11-03 10:42:14 -08001370 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -08001371 routerIpv4 = config.getRouterIpv4(deviceId);
1372 routerIpv6 = config.getRouterIpv6(deviceId);
Charles Chanef8d12e2017-12-05 21:07:38 -08001373 routerLinkLocalIpv6 = Ip6Address.valueOf(
1374 IPv6.getLinkLocalAddress(config.getDeviceMac(deviceId).toBytes()));
1375
Saurav Das261c3002017-06-13 15:35:54 -07001376 if (config.isPairedEdge(deviceId)) {
1377 pairRouterIpv4 = config.getRouterIpv4(config.getPairDeviceId(deviceId));
1378 pairRouterIpv6 = config.getRouterIpv6(config.getPairDeviceId(deviceId));
1379 }
Charles Chan319d1a22015-11-03 10:42:14 -08001380 } catch (DeviceConfigNotFoundException e) {
Danny Chang44a7b952020-09-08 15:27:04 -07001381 log.warn(e.getMessage() + " Aborting manageIpPunts.");
Charles Chan319d1a22015-11-03 10:42:14 -08001382 return;
1383 }
1384
pierventre37dcf4c2021-09-16 18:43:06 +02001385 if (request && !srManager.shouldProgram(deviceId)) {
1386 log.debug("Not installing port-IP punts - not handling programming for dev:{} ",
Saurav Dasc28b3432015-10-30 17:45:38 -07001387 deviceId);
1388 return;
1389 }
Danny Chang44a7b952020-09-08 15:27:04 -07001390
Pier Ventreadb4ae62016-11-23 09:57:42 -08001391 Set<IpAddress> allIps = new HashSet<>(config.getPortIPs(deviceId));
1392 allIps.add(routerIpv4);
1393 if (routerIpv6 != null) {
1394 allIps.add(routerIpv6);
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001395 allIps.add(routerLinkLocalIpv6);
Pier Ventreadb4ae62016-11-23 09:57:42 -08001396 }
Saurav Das261c3002017-06-13 15:35:54 -07001397 if (pairRouterIpv4 != null) {
1398 allIps.add(pairRouterIpv4);
1399 }
1400 if (pairRouterIpv6 != null) {
1401 allIps.add(pairRouterIpv6);
1402 }
Pier Ventreadb4ae62016-11-23 09:57:42 -08001403 for (IpAddress ipaddr : allIps) {
Danny Chang44a7b952020-09-08 15:27:04 -07001404 manageSingleIpPunts(deviceId, ipaddr, request);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001405 }
1406 }
Charles Chan2d0bbcd2017-01-09 11:45:08 -08001407
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001408 /**
Danny Chang44a7b952020-09-08 15:27:04 -07001409 * Creates or removes a forwarding objective ( packet-request ) to punt all IP packets, destined to the
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001410 * specified IP address, which should be router's port IP address.
1411 *
1412 * @param deviceId the switch dpid for the router
1413 * @param ipAddress the IP address of the router's port
Danny Chang44a7b952020-09-08 15:27:04 -07001414 * @param request true to create a packet request, false to remove
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001415 */
Danny Chang44a7b952020-09-08 15:27:04 -07001416 void manageSingleIpPunts(DeviceId deviceId, IpAddress ipAddress, boolean request) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001417 TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipAddress);
1418 Optional<DeviceId> optDeviceId = Optional.of(deviceId);
Danny Chang44a7b952020-09-08 15:27:04 -07001419 if (request) {
1420 srManager.packetService.requestPackets(sbuilder.build(),
1421 PacketPriority.CONTROL, srManager.appId, optDeviceId);
1422 } else {
1423 srManager.packetService.cancelPackets(sbuilder.build(),
1424 PacketPriority.CONTROL, srManager.appId, optDeviceId);
Saurav Das9f1c42e2015-10-23 10:51:11 -07001425 }
sangho80f11cb2015-04-01 13:05:26 -07001426 }
1427
piera9941192019-04-24 16:12:47 +02001428 // Method for building an IPv4 selector
1429 private TrafficSelector.Builder buildIpv4Selector() {
1430 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
1431 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
1432 return selectorBuilder;
1433 }
1434
1435 // Method for building an IPv6 selector
1436 private TrafficSelector.Builder buildIpv6Selector() {
1437 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
1438 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
1439 return selectorBuilder;
1440 }
1441
1442 // Method for building an IPv4 or IPv6 selector from an IP address
Pier Ventreadb4ae62016-11-23 09:57:42 -08001443 private TrafficSelector.Builder buildIpSelectorFromIpAddress(IpAddress addressToMatch) {
1444 return buildIpSelectorFromIpPrefix(addressToMatch.toIpPrefix());
1445 }
1446
piera9941192019-04-24 16:12:47 +02001447 // Method for building an IPv4 or IPv6 selector from an IP prefix
Pier Ventreadb4ae62016-11-23 09:57:42 -08001448 private TrafficSelector.Builder buildIpSelectorFromIpPrefix(IpPrefix prefixToMatch) {
1449 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Pier Ventre229fd0b2016-10-31 16:49:19 -07001450 // If the prefix is IPv4
Pier Ventreadb4ae62016-11-23 09:57:42 -08001451 if (prefixToMatch.isIp4()) {
1452 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
1453 selectorBuilder.matchIPDst(prefixToMatch.getIp4Prefix());
1454 return selectorBuilder;
1455 }
Pier Ventre229fd0b2016-10-31 16:49:19 -07001456 // If the prefix is IPv6
Pier Ventreadb4ae62016-11-23 09:57:42 -08001457 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
1458 selectorBuilder.matchIPv6Dst(prefixToMatch.getIp6Prefix());
1459 return selectorBuilder;
1460 }
1461
1462 /**
Pier Luigib9632ba2017-01-12 18:14:58 -08001463 * Creates forwarding objectives to punt ARP and NDP packets, to the controller.
1464 * Furthermore, these are applied only by the master instance. Deferred actions
1465 * are not cleared such that packets can be flooded in the cross connect use case
1466 *
1467 * @param deviceId the switch dpid for the router
1468 */
Charles Chan3ed34d82017-06-22 18:03:14 -07001469 void populateArpNdpPunts(DeviceId deviceId) {
Pier Ventre229fd0b2016-10-31 16:49:19 -07001470 // We are not the master just skip.
pierventre37dcf4c2021-09-16 18:43:06 +02001471 if (!srManager.shouldProgram(deviceId)) {
1472 log.debug("Not installing ARP/NDP punts - not handling programming for dev:{} ",
Pier Luigib9632ba2017-01-12 18:14:58 -08001473 deviceId);
1474 return;
1475 }
1476
Charles Chan3ed34d82017-06-22 18:03:14 -07001477 ForwardingObjective fwdObj;
Pier Luigib9632ba2017-01-12 18:14:58 -08001478 // We punt all ARP packets towards the controller.
Charles Chanef8d12e2017-12-05 21:07:38 -08001479 fwdObj = arpFwdObjective(null, true, ARP_NDP_PRIORITY)
Pier Luigib9632ba2017-01-12 18:14:58 -08001480 .add(new ObjectiveContext() {
1481 @Override
1482 public void onError(Objective objective, ObjectiveError error) {
Charles Chan3ed34d82017-06-22 18:03:14 -07001483 log.warn("Failed to install forwarding objective to punt ARP to {}: {}",
Pier Luigib9632ba2017-01-12 18:14:58 -08001484 deviceId, error);
1485 }
1486 });
Charles Chan3ed34d82017-06-22 18:03:14 -07001487 srManager.flowObjectiveService.forward(deviceId, fwdObj);
Pier Luigib9632ba2017-01-12 18:14:58 -08001488
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001489 if (isIpv6Configured(deviceId)) {
1490 // We punt all NDP packets towards the controller.
1491 ndpFwdObjective(null, true, ARP_NDP_PRIORITY).forEach(builder -> {
1492 ForwardingObjective obj = builder.add(new ObjectiveContext() {
1493 @Override
1494 public void onError(Objective objective, ObjectiveError error) {
1495 log.warn("Failed to install forwarding objective to punt NDP to {}: {}",
1496 deviceId, error);
1497 }
1498 });
1499 srManager.flowObjectiveService.forward(deviceId, obj);
Charles Chan051490d2018-01-11 11:48:18 -08001500 });
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001501 }
Charles Chan3ed34d82017-06-22 18:03:14 -07001502
Saurav Dasec683dc2018-04-27 18:42:30 -07001503 srManager.getPairLocalPort(deviceId).ifPresent(port -> {
Charles Chan3ed34d82017-06-22 18:03:14 -07001504 ForwardingObjective pairFwdObj;
1505 // Do not punt ARP packets from pair port
1506 pairFwdObj = arpFwdObjective(port, false, PacketPriority.CONTROL.priorityValue() + 1)
1507 .add(new ObjectiveContext() {
1508 @Override
1509 public void onError(Objective objective, ObjectiveError error) {
1510 log.warn("Failed to install forwarding objective to ignore ARP to {}: {}",
1511 deviceId, error);
1512 }
1513 });
1514 srManager.flowObjectiveService.forward(deviceId, pairFwdObj);
1515
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001516 if (isIpv6Configured(deviceId)) {
1517 // Do not punt NDP packets from pair port
1518 ndpFwdObjective(port, false, PacketPriority.CONTROL.priorityValue() + 1).forEach(builder -> {
1519 ForwardingObjective obj = builder.add(new ObjectiveContext() {
Charles Chan3ed34d82017-06-22 18:03:14 -07001520 @Override
1521 public void onError(Objective objective, ObjectiveError error) {
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001522 log.warn("Failed to install forwarding objective to ignore ARP to {}: {}",
Charles Chan3ed34d82017-06-22 18:03:14 -07001523 deviceId, error);
1524 }
1525 });
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001526 srManager.flowObjectiveService.forward(deviceId, obj);
1527 });
1528
1529 // Do not forward DAD packets from pair port
1530 pairFwdObj = dad6FwdObjective(port, PacketPriority.CONTROL.priorityValue() + 2)
1531 .add(new ObjectiveContext() {
1532 @Override
1533 public void onError(Objective objective, ObjectiveError error) {
1534 log.warn("Failed to install forwarding objective to drop DAD to {}: {}",
1535 deviceId, error);
1536 }
1537 });
1538 srManager.flowObjectiveService.forward(deviceId, pairFwdObj);
1539 }
Charles Chan3ed34d82017-06-22 18:03:14 -07001540 });
Pier Luigib9632ba2017-01-12 18:14:58 -08001541 }
1542
Charles Chan3ed34d82017-06-22 18:03:14 -07001543 private ForwardingObjective.Builder fwdObjBuilder(TrafficSelector selector,
1544 TrafficTreatment treatment, int priority) {
Pier Luigib9632ba2017-01-12 18:14:58 -08001545 return DefaultForwardingObjective.builder()
Charles Chan3ed34d82017-06-22 18:03:14 -07001546 .withPriority(priority)
Pier Luigib9632ba2017-01-12 18:14:58 -08001547 .withSelector(selector)
1548 .fromApp(srManager.appId)
1549 .withFlag(ForwardingObjective.Flag.VERSATILE)
Charles Chan3ed34d82017-06-22 18:03:14 -07001550 .withTreatment(treatment)
Pier Luigib9632ba2017-01-12 18:14:58 -08001551 .makePermanent();
1552 }
1553
Charles Chan3ed34d82017-06-22 18:03:14 -07001554 private ForwardingObjective.Builder arpFwdObjective(PortNumber port, boolean punt, int priority) {
Pier Luigib9632ba2017-01-12 18:14:58 -08001555 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
1556 sBuilder.matchEthType(TYPE_ARP);
Charles Chan3ed34d82017-06-22 18:03:14 -07001557 if (port != null) {
1558 sBuilder.matchInPort(port);
1559 }
Pier Luigib9632ba2017-01-12 18:14:58 -08001560
Charles Chan3ed34d82017-06-22 18:03:14 -07001561 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1562 if (punt) {
1563 tBuilder.punt();
Charles Chan90dd9322021-06-07 18:52:48 -07001564 } else {
1565 tBuilder.wipeDeferred();
Charles Chan3ed34d82017-06-22 18:03:14 -07001566 }
1567 return fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority);
Pier Luigib9632ba2017-01-12 18:14:58 -08001568 }
1569
Charles Chan051490d2018-01-11 11:48:18 -08001570 private Set<ForwardingObjective.Builder> ndpFwdObjective(PortNumber port, boolean punt, int priority) {
1571 Set<ForwardingObjective.Builder> result = Sets.newHashSet();
Pier Luigib9632ba2017-01-12 18:14:58 -08001572
Charles Chan051490d2018-01-11 11:48:18 -08001573 Lists.newArrayList(NEIGHBOR_SOLICITATION, NEIGHBOR_ADVERTISEMENT, ROUTER_SOLICITATION, ROUTER_ADVERTISEMENT)
1574 .forEach(type -> {
1575 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
1576 sBuilder.matchEthType(TYPE_IPV6)
1577 .matchIPProtocol(PROTOCOL_ICMP6)
1578 .matchIcmpv6Type(type);
1579 if (port != null) {
1580 sBuilder.matchInPort(port);
1581 }
1582
1583 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1584 if (punt) {
1585 tBuilder.punt();
Charles Chan90dd9322021-06-07 18:52:48 -07001586 } else {
1587 tBuilder.wipeDeferred();
Charles Chan051490d2018-01-11 11:48:18 -08001588 }
1589
1590 result.add(fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority));
1591 });
1592
1593 return result;
Charles Chan3ed34d82017-06-22 18:03:14 -07001594 }
1595
1596 private ForwardingObjective.Builder dad6FwdObjective(PortNumber port, int priority) {
1597 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
1598 sBuilder.matchEthType(TYPE_IPV6)
Charles Chane6bda752017-08-07 12:39:03 -07001599 .matchIPv6Src(Ip6Address.ZERO.toIpPrefix());
1600 // TODO CORD-1672 Fix this when OFDPA can distinguish ::/0 and ::/128 correctly
1601 // .matchIPProtocol(PROTOCOL_ICMP6)
1602 // .matchIcmpv6Type(NEIGHBOR_SOLICITATION);
Charles Chan3ed34d82017-06-22 18:03:14 -07001603 if (port != null) {
1604 sBuilder.matchInPort(port);
1605 }
1606
1607 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1608 tBuilder.wipeDeferred();
1609 return fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority);
Pier Luigib9632ba2017-01-12 18:14:58 -08001610 }
1611
1612 /**
Charles Chan155ec442018-09-16 14:30:19 -07001613 * Block given prefix in routing table.
Charles Chanf4586112015-11-09 16:37:23 -08001614 *
Andrea Campanella60ce2222018-04-30 11:48:55 +02001615 * @param address the address to block
1616 * @param deviceId switch ID to set the rules
1617 */
1618 void populateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
1619 updateDefaultRouteBlackhole(deviceId, address, true);
1620 }
1621
1622 /**
Charles Chan155ec442018-09-16 14:30:19 -07001623 * Unblock given prefix in routing table.
Andrea Campanella60ce2222018-04-30 11:48:55 +02001624 *
1625 * @param address the address to block
1626 * @param deviceId switch ID to set the rules
1627 */
1628 void removeDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
1629 updateDefaultRouteBlackhole(deviceId, address, false);
1630 }
1631
1632 private void updateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address, boolean install) {
1633 try {
1634 if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
1635
1636 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
1637 if (address.isIp4()) {
1638 sbuilder.matchIPDst(address);
1639 sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
1640 } else {
1641 sbuilder.matchIPv6Dst(address);
1642 sbuilder.matchEthType(EthType.EtherType.IPV6.ethType().toShort());
1643 }
1644
1645 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
Andrea Campanella60ce2222018-04-30 11:48:55 +02001646 tBuilder.wipeDeferred();
1647
1648 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
1649 fob.withFlag(Flag.SPECIFIC)
1650 .withSelector(sbuilder.build())
1651 .withTreatment(tBuilder.build())
1652 .withPriority(getPriorityFromPrefix(address))
1653 .fromApp(srManager.appId)
1654 .makePermanent();
1655
1656 log.debug("{} blackhole forwarding objectives for dev: {}",
1657 install ? "Installing" : "Removing", deviceId);
1658 ObjectiveContext context = new DefaultObjectiveContext(
1659 (objective) -> log.debug("Forward for {} {}", deviceId,
1660 install ? "installed" : "removed"),
1661 (objective, error) -> log.warn("Failed to {} forward for {}: {}",
1662 install ? "install" : "remove", deviceId, error));
1663 if (install) {
1664 srManager.flowObjectiveService.forward(deviceId, fob.add(context));
1665 } else {
1666 srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
1667 }
1668 }
1669 } catch (DeviceConfigNotFoundException e) {
1670 log.info("Not populating blackhole for un-configured device {}", deviceId);
1671 }
1672
1673 }
1674
1675 /**
1676 * Populates a forwarding objective to send packets that miss other high
1677 * priority Bridging Table entries to a group that contains all ports of
1678 * its subnet.
1679 *
Charles Chanf4586112015-11-09 16:37:23 -08001680 * @param deviceId switch ID to set the rules
1681 */
Charles Chan3ed34d82017-06-22 18:03:14 -07001682 void populateSubnetBroadcastRule(DeviceId deviceId) {
Charles Chan90772a72017-02-08 15:52:08 -08001683 srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001684 updateSubnetBroadcastRule(deviceId, vlanId, true);
Charles Chanf4586112015-11-09 16:37:23 -08001685 });
1686 }
1687
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001688 /**
1689 * Creates or removes a forwarding objective to broadcast packets to its subnet.
1690 * @param deviceId switch ID to set the rule
1691 * @param vlanId vlan ID to specify the subnet
1692 * @param install true to install the rule, false to revoke the rule
1693 */
1694 void updateSubnetBroadcastRule(DeviceId deviceId, VlanId vlanId, boolean install) {
1695 int nextId = srManager.getVlanNextObjectiveId(deviceId, vlanId);
1696
1697 if (nextId < 0) {
1698 log.error("Cannot install vlan {} broadcast rule in dev:{} due"
1699 + " to vlanId:{} or nextId:{}", vlanId, deviceId, vlanId, nextId);
1700 return;
1701 }
1702
1703 // Driver should treat objective with MacAddress.NONE as the
1704 // subnet broadcast rule
1705 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
1706 sbuilder.matchVlanId(vlanId);
1707 sbuilder.matchEthDst(MacAddress.NONE);
1708
1709 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
1710 fob.withFlag(Flag.SPECIFIC)
1711 .withSelector(sbuilder.build())
1712 .nextStep(nextId)
1713 .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
1714 .fromApp(srManager.appId)
1715 .makePermanent();
1716 ObjectiveContext context = new DefaultObjectiveContext(
pierventrea3989be2021-01-08 16:43:17 +01001717 (objective) -> log.debug("Vlan broadcast rule for {} {}", vlanId, install ?
1718 "populated" : "removed"),
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001719 (objective, error) ->
pierventrea3989be2021-01-08 16:43:17 +01001720 log.warn("Failed to {} vlan broadcast rule for {}: {}", install ? "populate" : "remove",
1721 vlanId, error));
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001722
1723 if (install) {
1724 srManager.flowObjectiveService.forward(deviceId, fob.add(context));
1725 } else {
1726 srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
1727 }
1728 }
1729
Charles Chan82ab1932016-01-30 23:22:37 -08001730 private int getPriorityFromPrefix(IpPrefix prefix) {
1731 return (prefix.isIp4()) ?
1732 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
1733 500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
Srikanth Vavilapalli8c83f1d2015-05-22 13:47:31 -07001734 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001735
1736 /**
1737 * Update Forwarding objective for each host and IP address connected to given port.
1738 * And create corresponding Simple Next objective if it does not exist.
1739 * Applied only when populating Forwarding objective
1740 * @param deviceId switch ID to set the rule
1741 * @param portNumber port number
1742 * @param prefix IP prefix of the route
1743 * @param hostMac MAC address of the next hop
1744 * @param vlanId Vlan ID of the port
1745 * @param popVlan true to pop vlan tag in TrafficTreatment
1746 * @param install true to populate the forwarding objective, false to revoke
pierventrea3989be2021-01-08 16:43:17 +01001747 * @return a completable future that completes when the fwdobj completes. In case of removal,
1748 * the completable future completes when also the nextobj completes.
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001749 */
pierventrea3989be2021-01-08 16:43:17 +01001750 CompletableFuture<Objective> updateFwdObj(DeviceId deviceId, PortNumber portNumber, IpPrefix prefix,
1751 MacAddress hostMac, VlanId vlanId, boolean popVlan, boolean install) {
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001752 ForwardingObjective.Builder fob;
1753 TrafficSelector.Builder sbuilder = buildIpSelectorFromIpPrefix(prefix);
1754 MacAddress deviceMac;
1755 try {
1756 deviceMac = config.getDeviceMac(deviceId);
1757 } catch (DeviceConfigNotFoundException e) {
1758 log.warn(e.getMessage() + " Aborting updateFwdObj.");
pierventrea3989be2021-01-08 16:43:17 +01001759 return CompletableFuture.completedFuture(null);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001760 }
1761
1762 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
1763 tbuilder.deferred()
1764 .setEthDst(hostMac)
1765 .setEthSrc(deviceMac)
1766 .setOutput(portNumber);
1767
1768 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
1769
1770 if (!popVlan) {
pierventre50e1bb82020-12-14 19:31:03 +01001771 mbuilder.matchVlanId(vlanId);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001772 tbuilder.setVlanId(vlanId);
1773 } else {
1774 mbuilder.matchVlanId(vlanId);
pierventre50e1bb82020-12-14 19:31:03 +01001775 tbuilder.popVlan();
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001776 }
1777
1778 // if the objective is to revoke an existing rule, and for some reason
1779 // the next-objective does not exist, then a new one should not be created
1780 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNumber,
1781 tbuilder.build(), mbuilder.build(), install);
pierventrea3989be2021-01-08 16:43:17 +01001782 CompletableFuture<Objective> future = new CompletableFuture<>();
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001783 if (portNextObjId == -1) {
1784 // Warning log will come from getPortNextObjective method
pierventrea3989be2021-01-08 16:43:17 +01001785 return CompletableFuture.completedFuture(null);
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001786 }
1787
1788 fob = DefaultForwardingObjective.builder().withSelector(sbuilder.build())
1789 .nextStep(portNextObjId).fromApp(srManager.appId).makePermanent()
1790 .withPriority(getPriorityFromPrefix(prefix)).withFlag(ForwardingObjective.Flag.SPECIFIC);
1791
1792 ObjectiveContext context = new DefaultObjectiveContext(
pierventrea3989be2021-01-08 16:43:17 +01001793 (objective) -> {
1794 log.debug("IP rule for route {} {}", prefix, install ? "installed" : "revoked");
1795 future.complete(objective);
1796 },
1797 (objective, error) -> {
1798 log.warn("Failed to {} IP rule for route {}: {}", install ? "install" : "revoke",
1799 prefix, error);
1800 future.complete(null);
1801 });
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001802 srManager.flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
1803
1804 if (!install) {
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001805 if (!srManager.getVlanPortMap(deviceId).containsKey(vlanId) ||
1806 !srManager.getVlanPortMap(deviceId).get(vlanId).contains(portNumber)) {
1807 DefaultGroupHandler grpHandler = srManager.getGroupHandler(deviceId);
1808 if (grpHandler == null) {
1809 log.warn("updateFwdObj: groupHandler for device {} not found", deviceId);
1810 } else {
pierventrea3989be2021-01-08 16:43:17 +01001811 // Before moving forward we have to be sure flow has been removed;
1812 try {
1813 future.get();
1814 } catch (InterruptedException | ExecutionException e) {
1815 log.warn("Exception caught when executing IP rule removal for route {}", prefix);
1816 }
1817 // Remove L3UG for the given port and host and return the future
1818 // Flow future has been already consumed (normally or exceptionally)
1819 return grpHandler.removeGroupFromPort(portNumber, tbuilder.build(), mbuilder.build());
Shibu Vijayakumar632dd642018-03-01 15:45:59 -08001820 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001821 }
1822 }
pierventrea3989be2021-01-08 16:43:17 +01001823 return future;
Jonghwan Hyune5ef7622017-08-25 17:48:36 -07001824 }
Charles Chanf17f66b2018-02-26 21:33:25 -08001825
1826 /**
1827 * Checks if there is other enabled port within the given VLAN on the given device.
1828 *
1829 * @param deviceId device ID
1830 * @param vlanId VLAN ID
1831 * @return true if there is no more port enabled within the given VLAN on the given device
1832 */
1833 boolean noMoreEnabledPort(DeviceId deviceId, VlanId vlanId) {
1834 Set<ConnectPoint> enabledPorts = srManager.deviceService.getPorts(deviceId).stream()
1835 .filter(Port::isEnabled)
1836 .map(port -> new ConnectPoint(port.element().id(), port.number()))
1837 .collect(Collectors.toSet());
1838
1839 return enabledPorts.stream().noneMatch(cp ->
1840 // Given vlanId is included in the vlan-tagged configuration
Charles Chan098ca202018-05-01 11:50:20 -07001841 srManager.interfaceService.getTaggedVlanId(cp).contains(vlanId) ||
pieraac79e92019-10-04 15:40:34 +02001842 // Given vlanId is INTERNAL_VLAN or PSEUDOWIRE_VLAN and the interface is not configured
Charles Chan098ca202018-05-01 11:50:20 -07001843 (srManager.interfaceService.getTaggedVlanId(cp).isEmpty() && srManager.getInternalVlanId(cp) == null &&
pieraac79e92019-10-04 15:40:34 +02001844 (vlanId.equals(srManager.getDefaultInternalVlan()) || vlanId.equals(srManager.getPwTransportVlan()))) ||
Charles Chanf17f66b2018-02-26 21:33:25 -08001845 // interface is configured and either vlan-untagged or vlan-native matches given vlanId
1846 (srManager.getInternalVlanId(cp) != null && srManager.getInternalVlanId(cp).equals(vlanId))
1847 );
1848 }
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001849
1850 /**
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001851 * Populates IP rules for a route that has double-tagged next hop.
1852 *
1853 * @param deviceId device ID of the device that next hop attaches to
1854 * @param prefix IP prefix of the route
1855 * @param hostMac MAC address of the next hop
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001856 * @param innerVlan inner Vlan ID of the next hop
1857 * @param outerVlan outer Vlan ID of the next hop
1858 * @param outerTpid outer TPID of the next hop
1859 * @param outPort port where the next hop attaches to
1860 */
Charles Chan61c086d2019-07-26 17:46:15 -07001861 void populateDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001862 VlanId innerVlan, VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
1863 ForwardingObjective.Builder fwdBuilder;
1864 log.debug("Populate direct routing entry for double-tagged host route {} at {}:{}",
1865 prefix, deviceId, outPort);
1866
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001867 try {
Charles Chan61c086d2019-07-26 17:46:15 -07001868 fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, outerVlan, outPort, innerVlan, outerTpid,
1869 true, false);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001870 } catch (DeviceConfigNotFoundException e) {
1871 log.error(e.getMessage() + " Aborting populateDoubleTaggedRoute");
1872 return;
1873 }
1874 if (fwdBuilder == null) {
1875 log.error("Aborting double-tagged host routing table entry due to error for dev:{} route:{}",
1876 deviceId, prefix);
1877 return;
1878 }
1879
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001880 int nextId = fwdBuilder.add().nextId();
1881 DefaultObjectiveContext context = new DefaultObjectiveContext(objective -> {
1882 log.debug("Direct routing rule for double-tagged host route {} populated. nextId={}", prefix, nextId);
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001883 }, (objective, error) ->
1884 log.warn("Failed to populate direct routing rule for double-tagged host route {}: {}", prefix, error)
1885 );
1886 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
1887 rulePopulationCounter.incrementAndGet();
1888 }
1889
1890 /**
1891 * Revokes IP rules for a route that has double-tagged next hop.
1892 *
1893 * @param deviceId device ID of the device that next hop attaches to
1894 * @param prefix IP prefix of the route
1895 * @param hostMac MAC address of the next hop
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001896 * @param innerVlan inner Vlan ID of the next hop
1897 * @param outerVlan outer Vlan ID of the next hop
1898 * @param outerTpid outer TPID of the next hop
1899 * @param outPort port where the next hop attaches to
1900 */
1901 void revokeDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
Charles Chan61c086d2019-07-26 17:46:15 -07001902 VlanId innerVlan, VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
1903 ForwardingObjective.Builder fwdBuilder;
1904 log.debug("Revoking direct routing entry for double-tagged host route {} at {}:{}",
1905 prefix, deviceId, outPort);
Charles Chan61c086d2019-07-26 17:46:15 -07001906 try {
1907 fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, outerVlan, outPort, innerVlan, outerTpid,
1908 true, true);
1909 } catch (DeviceConfigNotFoundException e) {
1910 log.error(e.getMessage() + " Aborting revokeDoubleTaggedRoute");
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001911 return;
1912 }
Charles Chan61c086d2019-07-26 17:46:15 -07001913 if (fwdBuilder == null) {
1914 log.error("Aborting double-tagged host routing table entry due to error for dev:{} route:{}",
1915 deviceId, prefix);
1916 return;
1917 }
1918
1919 int nextId = fwdBuilder.remove().nextId();
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001920 DefaultObjectiveContext context = new DefaultObjectiveContext(objective -> {
Charles Chan61c086d2019-07-26 17:46:15 -07001921 log.debug("Direct routing rule for double-tagged host route {} revoked. nextId={}", prefix, nextId);
Charles Chan61c086d2019-07-26 17:46:15 -07001922 // Try to remove next objective as well
1923 ImmutablePair<TrafficTreatment, TrafficSelector> treatmentAndMeta;
1924 try {
1925 treatmentAndMeta = getTreatmentAndMeta(deviceId, hostMac, outerVlan, outPort, innerVlan, outerTpid);
1926 } catch (DeviceConfigNotFoundException e) {
1927 log.error(e.getMessage() + " Aborting revokeDoubleTaggedRoute");
1928 return;
1929 }
Charles Chan61c086d2019-07-26 17:46:15 -07001930 if (treatmentAndMeta == null) {
1931 // Warning log will come from getTreatmentAndMeta method
1932 return;
1933 }
Charles Chan61c086d2019-07-26 17:46:15 -07001934 DefaultGroupHandler groupHandler = srManager.getGroupHandler(deviceId);
1935 if (groupHandler == null) {
1936 log.warn("Failed to revoke direct routing rule for double-tagged host route {}: " +
1937 "group handler not found for {}", prefix, deviceId);
1938 return;
1939 }
1940 groupHandler.removeGroupFromPort(outPort, treatmentAndMeta.getLeft(), treatmentAndMeta.getRight());
1941
1942 }, (objective, error) ->
1943 log.warn("Failed to revoke direct routing rule for double-tagged host route {}: {}", prefix, error)
1944 );
1945 srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
Jonghwan Hyun9aaa34f2018-04-09 09:40:50 -07001946 }
1947
Jonghwan Hyun671a7ab2018-04-30 09:27:21 -07001948 /**
1949 * Checks whether the specified port has IP configuration or not.
1950 *
1951 * @param cp ConnectPoint to check the existance of IP configuration
1952 * @return true if the port has IP configuration; false otherwise.
1953 */
1954 private boolean hasIPConfiguration(ConnectPoint cp) {
1955 Set<Interface> interfaces = srManager.interfaceService.getInterfacesByPort(cp);
1956 return interfaces.stream().anyMatch(intf -> intf.ipAddressesList().size() > 0);
1957 }
Saurav Das9bf49582018-08-13 15:34:26 -07001958
1959 /**
1960 * Updates filtering rules for unconfigured ports on all devices for which
1961 * this controller instance is master.
1962 *
1963 * @param pushVlan true if the filtering rule requires a push vlan action
1964 * @param oldVlanId the vlanId to be removed
1965 * @param newVlanId the vlanId to be added
1966 */
1967 void updateSpecialVlanFilteringRules(boolean pushVlan, VlanId oldVlanId,
1968 VlanId newVlanId) {
1969 for (Device dev : srManager.deviceService.getAvailableDevices()) {
pierventre37dcf4c2021-09-16 18:43:06 +02001970 if (srManager.shouldProgram(dev.id())) {
Saurav Das9bf49582018-08-13 15:34:26 -07001971 for (Port p : srManager.deviceService.getPorts(dev.id())) {
1972 if (!hasIPConfiguration(new ConnectPoint(dev.id(), p.number()))
1973 && p.isEnabled()) {
1974 updateSinglePortFilters(dev.id(), p.number(), pushVlan,
1975 oldVlanId, false);
1976 updateSinglePortFilters(dev.id(), p.number(), pushVlan,
1977 newVlanId, true);
1978 }
1979 }
1980 }
1981 }
1982 }
1983
Charles Chanf0f7b5c2018-08-29 14:55:53 -07001984 private boolean isIpv6Configured(DeviceId deviceId) {
1985 boolean isIpv6Configured;
1986 try {
1987 isIpv6Configured = (config.getRouterIpv6(deviceId) != null);
1988 } catch (DeviceConfigNotFoundException e) {
1989 isIpv6Configured = false;
1990 }
1991 return isIpv6Configured;
1992 }
sangho80f11cb2015-04-01 13:05:26 -07001993}