blob: 1379e79fa9ba71bb7a8841a371566255d482b098 [file] [log] [blame]
Charles Chan1eaf4802016-04-18 13:44:03 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chan1eaf4802016-04-18 13:44:03 -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 */
16
17package org.onosproject.segmentrouting;
18
Charles Chan1eaf4802016-04-18 13:44:03 -070019import org.onlab.packet.IpAddress;
Jonghwan Hyune5ef7622017-08-25 17:48:36 -070020import org.onlab.packet.IpPrefix;
Charles Chan1eaf4802016-04-18 13:44:03 -070021import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
Charles Chan10b0fb72017-02-02 16:20:42 -080023import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070024import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070025import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070026import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070027import org.onosproject.net.PortNumber;
28import org.onosproject.net.flow.DefaultTrafficSelector;
29import org.onosproject.net.flow.DefaultTrafficTreatment;
30import org.onosproject.net.flow.TrafficSelector;
31import org.onosproject.net.flow.TrafficTreatment;
32import org.onosproject.net.flowobjective.DefaultForwardingObjective;
33import org.onosproject.net.flowobjective.DefaultObjectiveContext;
34import org.onosproject.net.flowobjective.FlowObjectiveService;
35import org.onosproject.net.flowobjective.ForwardingObjective;
36import org.onosproject.net.flowobjective.ObjectiveContext;
37import org.onosproject.net.host.HostEvent;
Charles Chan873661e2017-11-30 15:37:50 -080038import org.onosproject.net.host.HostLocationProbingService.ProbeMode;
Charles Chan1eaf4802016-04-18 13:44:03 -070039import org.onosproject.net.host.HostService;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Saurav Dasf9332192017-02-18 14:05:44 -080043import com.google.common.collect.Sets;
Charles Chan3ed34d82017-06-22 18:03:14 -070044
45import java.util.Optional;
Charles Chan1eaf4802016-04-18 13:44:03 -070046import java.util.Set;
Charles Chan873661e2017-11-30 15:37:50 -080047import java.util.concurrent.Executors;
48import java.util.concurrent.ScheduledExecutorService;
49import java.util.concurrent.TimeUnit;
Charles Chand9265a32017-06-16 15:19:24 -070050import java.util.stream.Collectors;
51
52import static com.google.common.base.Preconditions.checkArgument;
Charles Chan1eaf4802016-04-18 13:44:03 -070053
54/**
55 * Handles host-related events.
56 */
57public class HostHandler {
58 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan873661e2017-11-30 15:37:50 -080059 static final int HOST_MOVED_DELAY_MS = 1000;
60
Charles Chan114aec72017-06-19 14:00:53 -070061 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070062 private HostService hostService;
63 private FlowObjectiveService flowObjectiveService;
64
65 /**
66 * Constructs the HostHandler.
67 *
68 * @param srManager Segment Routing manager
69 */
Charles Chand9265a32017-06-16 15:19:24 -070070 HostHandler(SegmentRoutingManager srManager) {
Charles Chan1eaf4802016-04-18 13:44:03 -070071 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070072 hostService = srManager.hostService;
73 flowObjectiveService = srManager.flowObjectiveService;
74 }
75
Charles Chandebfea32016-10-24 14:52:01 -070076 protected void init(DeviceId devId) {
Charles Chand9265a32017-06-16 15:19:24 -070077 hostService.getHosts().forEach(host ->
78 host.locations().stream()
Charles Chan873661e2017-11-30 15:37:50 -080079 .filter(location -> location.deviceId().equals(devId) ||
80 location.deviceId().equals(srManager.getPairDeviceId(devId).orElse(null)))
Charles Chand9265a32017-06-16 15:19:24 -070081 .forEach(location -> processHostAddedAtLocation(host, location))
82 );
Charles Chanc22cef32016-04-29 14:38:22 -070083 }
Charles Chan1eaf4802016-04-18 13:44:03 -070084
Charles Chand9265a32017-06-16 15:19:24 -070085 void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070086 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070087 }
88
Charles Chand9265a32017-06-16 15:19:24 -070089 private void processHostAdded(Host host) {
90 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Charles Chan1eaf4802016-04-18 13:44:03 -070091 }
92
Charles Chand9265a32017-06-16 15:19:24 -070093 void processHostAddedAtLocation(Host host, HostLocation location) {
94 checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
95
Charles Chanceb2a2e2017-09-12 18:57:47 -070096 MacAddress hostMac = host.mac();
97 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -070098 Set<HostLocation> locations = host.locations();
99 Set<IpAddress> ips = host.ipAddresses();
Charles Chanceb2a2e2017-09-12 18:57:47 -0700100 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chand9265a32017-06-16 15:19:24 -0700101
Charles Chanceb2a2e2017-09-12 18:57:47 -0700102 if (srManager.isMasterOf(location)) {
103 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
104 ips.forEach(ip ->
105 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
106 );
107 }
108
109 // Use the pair link temporarily before the second location of a dual-homed host shows up.
110 // This do not affect single-homed hosts since the flow will be blocked in
111 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
112 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
113 if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
114 host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
115 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
116 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
117 // when the host is untagged
118 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
119
120 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
121 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
122 ip, false));
Charles Chan873661e2017-11-30 15:37:50 -0800123
124 if (srManager.activeProbing) {
125 probe(host, location, pairDeviceId, pairRemotePort);
126 }
Charles Chanceb2a2e2017-09-12 18:57:47 -0700127 });
128 }
129 });
Charles Chand9265a32017-06-16 15:19:24 -0700130 }
131
132 void processHostRemovedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700133 processHostRemoved(event.subject());
134 }
135
Charles Chand9265a32017-06-16 15:19:24 -0700136 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700137 MacAddress hostMac = host.mac();
138 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700139 Set<HostLocation> locations = host.locations();
Charles Chan41f5ec02016-06-13 18:54:31 -0700140 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700141 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700142
Charles Chan910be6a2017-08-23 14:46:43 -0700143 locations.forEach(location -> {
144 if (srManager.isMasterOf(location)) {
145 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
146 ips.forEach(ip ->
147 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
148 );
149 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700150
151 // Also remove redirection flows on the pair device if exists.
152 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
153 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(location.deviceId());
Charles Chan910be6a2017-08-23 14:46:43 -0700154 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
155 srManager.mastershipService.isLocalMaster(pairDeviceId.get())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700156 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
157 // when the host is untagged
158 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
159
160 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
161 ips.forEach(ip ->
162 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
163 ip, true));
164 }
Charles Chand9265a32017-06-16 15:19:24 -0700165 });
Charles Chanc22cef32016-04-29 14:38:22 -0700166 }
167
Charles Chand9265a32017-06-16 15:19:24 -0700168 void processHostMovedEvent(HostEvent event) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700169 MacAddress hostMac = event.subject().mac();
170 VlanId hostVlanId = event.subject().vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700171 Set<HostLocation> prevLocations = event.prevSubject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700172 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chand9265a32017-06-16 15:19:24 -0700173 Set<HostLocation> newLocations = event.subject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700174 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chanc22cef32016-04-29 14:38:22 -0700175
Charles Chan873661e2017-11-30 15:37:50 -0800176 // FIXME: Delay event handling a little bit to wait for the previous redirection flows to be completed
177 // The permanent solution would be introducing CompletableFuture and wait for it
178 if (prevLocations.size() == 1 && newLocations.size() == 2) {
179 log.debug("Delay event handling when host {}/{} moves from 1 to 2 locations", hostMac, hostVlanId);
180 ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
181 executorService.schedule(() ->
182 processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps),
183 HOST_MOVED_DELAY_MS, TimeUnit.MILLISECONDS);
184 } else {
185 processHostMoved(hostMac, hostVlanId, prevLocations, prevIps, newLocations, newIps);
186 }
187 }
188
189 private void processHostMoved(MacAddress hostMac, VlanId hostVlanId, Set<HostLocation> prevLocations,
190 Set<IpAddress> prevIps, Set<HostLocation> newLocations, Set<IpAddress> newIps) {
191 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chand9265a32017-06-16 15:19:24 -0700192 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
193 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700194
Charles Chand9265a32017-06-16 15:19:24 -0700195 // For each old location
Charles Chan910be6a2017-08-23 14:46:43 -0700196 Sets.difference(prevLocations, newLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700197 .forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700198 // Remove routing rules for old IPs
199 Sets.difference(prevIps, newIps).forEach(ip ->
200 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
201 ip, true)
202 );
203
204 // Redirect the flows to pair link if configured
205 // Note: Do not continue removing any rule
206 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
207 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(prevLocation.deviceId());
208 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
209 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
210 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
211 // when the host is untagged
212 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
213
214 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
215 newIps.forEach(ip ->
216 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
217 ip, false));
218 return;
219 }
Charles Chand9265a32017-06-16 15:19:24 -0700220
221 // Remove bridging rule and routing rules for unchanged IPs if the host moves from a switch to another.
222 // Otherwise, do not remove and let the adding part update the old flow
223 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700224 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700225 Sets.intersection(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700226 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700227 ip, true)
228 );
229 }
230
231 // Remove bridging rules if new interface vlan is different from old interface vlan
232 // Otherwise, do not remove and let the adding part update the old flow
233 if (newLocations.stream().noneMatch(newLocation -> {
234 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
235 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
236 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan3ed34d82017-06-22 18:03:14 -0700237 return srManager.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700238 (oldAssignedVlan != null && newAssignedVlan != null &&
239 // Host is untagged and the new location has the same assigned vlan
240 oldAssignedVlan.equals(newAssignedVlan));
241 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700242 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700243 }
244
245 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
246 // the IP. Otherwise, do not remove and let the adding part update the old flow
247 Sets.intersection(prevIps, newIps).forEach(ip -> {
248 if (newLocations.stream().noneMatch(newLocation ->
249 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700250 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700251 ip, true);
252 }
Charles Chanc22cef32016-04-29 14:38:22 -0700253 });
Charles Chand9265a32017-06-16 15:19:24 -0700254 });
255
256 // For each new location, add all new IPs.
Charles Chan910be6a2017-08-23 14:46:43 -0700257 Sets.difference(newLocations, prevLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700258 .forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700259 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Charles Chand9265a32017-06-16 15:19:24 -0700260 newIps.forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700261 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700262 ip, false)
263 );
264 });
265
266 // For each unchanged location, add new IPs and remove old IPs.
Charles Chan910be6a2017-08-23 14:46:43 -0700267 Sets.intersection(newLocations, prevLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700268 .forEach(unchangedLocation -> {
Charles Chand9265a32017-06-16 15:19:24 -0700269 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700270 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
271 hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700272 );
273
274 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700275 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
276 hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700277 );
278 });
Charles Chanc22cef32016-04-29 14:38:22 -0700279 }
280
Charles Chand9265a32017-06-16 15:19:24 -0700281 void processHostUpdatedEvent(HostEvent event) {
Charles Chan873661e2017-11-30 15:37:50 -0800282 Host host = event.subject();
283 MacAddress hostMac = host.mac();
284 VlanId hostVlanId = host.vlan();
285 Set<HostLocation> locations = host.locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700286 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chan873661e2017-11-30 15:37:50 -0800287 Set<IpAddress> newIps = host.ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800288 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700289
Charles Chan910be6a2017-08-23 14:46:43 -0700290 locations.stream().filter(srManager::isMasterOf).forEach(location -> {
Charles Chand9265a32017-06-16 15:19:24 -0700291 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800292 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700293 );
294 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800295 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700296 );
297 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800298
299 // Use the pair link temporarily before the second location of a dual-homed host shows up.
300 // This do not affect single-homed hosts since the flow will be blocked in
301 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
Charles Chan873661e2017-11-30 15:37:50 -0800302 locations.forEach(location ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800303 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
304 if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
305 locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
Charles Chan873661e2017-11-30 15:37:50 -0800306 Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
307 Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
308
Charles Chanb3c1faf2017-11-20 08:46:24 -0800309 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
310 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
311 // when the host is untagged
312 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
313
Charles Chan873661e2017-11-30 15:37:50 -0800314 ipsToRemove.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800315 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
316 );
Charles Chan873661e2017-11-30 15:37:50 -0800317 ipsToAdd.forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800318 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
319 );
Charles Chan873661e2017-11-30 15:37:50 -0800320
321 if (srManager.activeProbing) {
322 probe(host, location, pairDeviceId, pairRemotePort);
323 }
Charles Chanb3c1faf2017-11-20 08:46:24 -0800324 });
325 }
Charles Chan873661e2017-11-30 15:37:50 -0800326 })
327 );
328 }
329
330 /**
331 * When a non-pair port comes up, probe each host on the pair device if
332 * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
333 * (2) the host is untagged and the internal vlan is the same on the host port and current port.
334 *
335 * @param cp connect point
336 */
337 void processPortUp(ConnectPoint cp) {
338 if (cp.port().equals(srManager.getPairLocalPorts(cp.deviceId()).orElse(null))) {
339 return;
340 }
341 if (srManager.activeProbing) {
342 srManager.getPairDeviceId(cp.deviceId())
343 .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).stream()
344 .filter(host -> isHostInVlanOfPort(host, pairDeviceId, cp))
345 .forEach(host -> srManager.probingService.probeHostLocation(host, cp, ProbeMode.DISCOVER))
346 );
347 }
348 }
349
350 /**
351 * Checks if given host located on given device id matches VLAN config of current port.
352 *
353 * @param host host to check
354 * @param deviceId device id to check
355 * @param cp current connect point
356 * @return true if the host located at deviceId matches the VLAN config on cp
357 */
358 private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
359 VlanId internalVlan = srManager.getInternalVlanId(cp);
360 Set<VlanId> taggedVlan = srManager.getTaggedVlanId(cp);
361
362 return taggedVlan.contains(host.vlan()) ||
363 (internalVlan != null && host.locations().stream()
364 .filter(l -> l.deviceId().equals(deviceId))
365 .map(srManager::getInternalVlanId)
366 .anyMatch(internalVlan::equals));
367 }
368
369 /**
370 * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
371 *
372 * @param host host to probe
373 * @param location newly discovered host location
374 * @param pairDeviceId pair device id
375 * @param pairRemotePort pair remote port
376 */
377 private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
378 VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
379 srManager.getInternalVlanId(location) : host.vlan();
380 srManager.interfaceService.getInterfaces().stream()
381 .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
382 i.vlanUntagged().equals(vlanToProbe) ||
383 i.vlanNative().equals(vlanToProbe))
384 .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
385 .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
386 .forEach(i -> {
387 log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
388 srManager.probingService.probeHostLocation(host, i.connectPoint(), ProbeMode.DISCOVER);
389 });
Charles Chanc22cef32016-04-29 14:38:22 -0700390 }
391
392 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800393 * Generates a forwarding objective builder for bridging rules.
394 * <p>
395 * The forwarding objective bridges packets destined to a given MAC to
396 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700397 *
398 * @param deviceId Device that host attaches to
399 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800400 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700401 * @param outport Port that host attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700402 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700403 * @return Forwarding objective builder
404 */
Charles Chand9265a32017-06-16 15:19:24 -0700405 ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800406 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700407 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800408 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
409 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
410 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
411 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700412
Charles Chan90772a72017-02-08 15:52:08 -0800413 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700414 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
415 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700416
Charles Chan90772a72017-02-08 15:52:08 -0800417 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700418 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700419 tbuilder.immediate().setOutput(outport);
420
Charles Chan90772a72017-02-08 15:52:08 -0800421 // Create host meta
422 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
423
424 // Adjust the selector, treatment and meta according to VLAN configuration
425 if (taggedVlans.contains(hostVlanId)) {
426 sbuilder.matchVlanId(hostVlanId);
427 mbuilder.matchVlanId(hostVlanId);
428 } else if (hostVlanId.equals(VlanId.NONE)) {
429 if (untaggedVlan != null) {
430 sbuilder.matchVlanId(untaggedVlan);
431 mbuilder.matchVlanId(untaggedVlan);
432 tbuilder.immediate().popVlan();
433 } else if (nativeVlan != null) {
434 sbuilder.matchVlanId(nativeVlan);
435 mbuilder.matchVlanId(nativeVlan);
436 tbuilder.immediate().popVlan();
437 } else {
Charles Chan184e0242017-05-26 14:23:58 -0700438 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
439 "vlan config", mac, hostVlanId, connectPoint);
440 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800441 }
442 } else {
443 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
444 mac, hostVlanId, connectPoint);
445 return null;
446 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700447
448 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700449 // If the objective is to revoke an existing rule, and for some reason
450 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700451 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700452 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700453 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800454 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700455 return null;
456 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700457
458 return DefaultForwardingObjective.builder()
459 .withFlag(ForwardingObjective.Flag.SPECIFIC)
460 .withSelector(sbuilder.build())
461 .nextStep(portNextObjId)
462 .withPriority(100)
463 .fromApp(srManager.appId)
464 .makePermanent();
465 }
466
Charles Chanc22cef32016-04-29 14:38:22 -0700467 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700468 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
469 * output to given port.
470 *
471 * @param deviceId device ID
472 * @param port port
473 * @param mac mac address
474 * @param vlanId VLAN ID
475 * @param revoke true to revoke the rule; false to populate
476 */
477 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
478 VlanId vlanId, boolean revoke) {
479 log.debug("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
480 mac, vlanId, deviceId, port);
481
482 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, revoke);
483 if (fob == null) {
484 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
485 return;
486 }
487
488 ObjectiveContext context = new DefaultObjectiveContext(
489 (objective) -> log.debug("Brigding rule for {}/{} {}", mac, vlanId,
490 revoke ? "revoked" : "populated"),
491 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
492 revoke ? "revoked" : "populated", mac, vlanId, error));
493 flowObjectiveService.forward(deviceId, revoke ? fob.remove(context) : fob.add(context));
494 }
495
496 /**
497 * Populate or revoke a routing rule on given deviceId that matches given ip,
498 * set destination mac to given mac, set vlan to given vlan and output to given port.
499 *
500 * @param deviceId device ID
501 * @param port port
502 * @param mac mac address
503 * @param vlanId VLAN ID
504 * @param ip IP address
505 * @param revoke true to revoke the rule; false to populate
506 */
507 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
508 VlanId vlanId, IpAddress ip, boolean revoke) {
509 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700510 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
511 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
512 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700513 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700514
Charles Chand9265a32017-06-16 15:19:24 -0700515 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
516 if (revoke) {
Charles Chan910be6a2017-08-23 14:46:43 -0700517 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700518 } else {
Charles Chan910be6a2017-08-23 14:46:43 -0700519 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan370a65b2016-05-10 17:29:47 -0700520 }
Charles Chan370a65b2016-05-10 17:29:47 -0700521 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700522
523 /**
524 * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
525 * and hostMAC connected to given port, and output to given port only when
526 * vlan information is valid.
527 *
528 * @param deviceId device ID that host attaches to
529 * @param portNum port number that host attaches to
530 * @param hostMac mac address of the host connected to the switch port
531 * @param vlanId Vlan ID configured on the switch port
532 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
533 * @param install true to populate the objective, false to revoke
534 */
535 private void updateBridgingRule(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
536 VlanId vlanId, boolean popVlan, boolean install) {
537 // Create host selector
538 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
539 sbuilder.matchEthDst(hostMac);
540
541 // Create host meta
542 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
543
544 sbuilder.matchVlanId(vlanId);
545 mbuilder.matchVlanId(vlanId);
546
547 // Create host treatment
548 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
549 tbuilder.immediate().setOutput(portNum);
550
551 if (popVlan) {
552 tbuilder.immediate().popVlan();
553 }
554
555 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
556 tbuilder.build(), mbuilder.build(), install);
557 if (portNextObjId != -1) {
558 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
559 .withFlag(ForwardingObjective.Flag.SPECIFIC)
560 .withSelector(sbuilder.build())
561 .nextStep(portNextObjId)
562 .withPriority(100)
563 .fromApp(srManager.appId)
564 .makePermanent();
565
566 ObjectiveContext context = new DefaultObjectiveContext(
567 (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
568 install ? "populated" : "revoked"),
569 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
570 install ? "populate" : "revoke", hostMac, vlanId, error));
571 flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
572 } else {
573 log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
574 }
575 }
576
577 /**
578 * Update forwarding objective for unicast bridging and unicast routing.
579 * Also check the validity of updated interface configuration on VLAN.
580 *
581 * @param deviceId device ID that host attaches to
582 * @param portNum port number that host attaches to
583 * @param vlanId Vlan ID configured on the switch port
584 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
585 * @param install true to populate the objective, false to revoke
586 */
587 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
588 boolean popVlan, boolean install) {
589 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
590 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
591
592 if (hosts == null || hosts.size() == 0) {
593 return;
594 }
595
596 hosts.forEach(host -> {
597 MacAddress mac = host.mac();
598 VlanId hostVlanId = host.vlan();
599
600 // Check whether the host vlan is valid for new interface configuration
601 if ((!popVlan && hostVlanId.equals(vlanId)) ||
602 (popVlan && hostVlanId.equals(VlanId.NONE))) {
603 updateBridgingRule(deviceId, portNum, mac, vlanId, popVlan, install);
604 // Update Forwarding objective and corresponding simple Next objective
605 // for each host and IP address connected to given port
606 host.ipAddresses().forEach(ipAddress ->
607 srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
608 mac, vlanId, popVlan, install)
609 );
610 }
611 });
612 }
613
614 /**
615 * Populate or revoke routing rule for each host, according to the updated
616 * subnet configuration on the interface.
617 * @param cp connect point of the updated interface
618 * @param ipPrefixSet IP Prefixes added or removed
619 * @param install true if IP Prefixes added, false otherwise
620 */
621 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
622 Set<Host> hosts = hostService.getConnectedHosts(cp);
623
624 if (hosts == null || hosts.size() == 0) {
625 log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
626 return;
627 }
628
629 // Check whether the host IP address is in the interface's subnet
630 hosts.forEach(host ->
631 host.ipAddresses().forEach(hostIpAddress -> {
632 ipPrefixSet.forEach(ipPrefix -> {
633 if (install && ipPrefix.contains(hostIpAddress)) {
634 srManager.routingRulePopulator.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
635 host.mac(), host.vlan(), cp.port());
636 } else if (!install && ipPrefix.contains(hostIpAddress)) {
637 srManager.routingRulePopulator.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
638 host.mac(), host.vlan(), cp.port());
639 }
640 });
641 }));
642 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700643}