blob: 6ac25f3ea1ed881f1322079624b87e947d3bcae4 [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;
38import org.onosproject.net.host.HostService;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Saurav Dasf9332192017-02-18 14:05:44 -080042import com.google.common.collect.Sets;
Charles Chan3ed34d82017-06-22 18:03:14 -070043
44import java.util.Optional;
Charles Chan1eaf4802016-04-18 13:44:03 -070045import java.util.Set;
Charles Chand9265a32017-06-16 15:19:24 -070046import java.util.stream.Collectors;
47
48import static com.google.common.base.Preconditions.checkArgument;
Charles Chan1eaf4802016-04-18 13:44:03 -070049
50/**
51 * Handles host-related events.
52 */
53public class HostHandler {
Charles Chanabfe7e02017-08-09 16:50:15 -070054
Charles Chan1eaf4802016-04-18 13:44:03 -070055 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan114aec72017-06-19 14:00:53 -070056 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070057 private HostService hostService;
58 private FlowObjectiveService flowObjectiveService;
59
60 /**
61 * Constructs the HostHandler.
62 *
63 * @param srManager Segment Routing manager
64 */
Charles Chand9265a32017-06-16 15:19:24 -070065 HostHandler(SegmentRoutingManager srManager) {
Charles Chan1eaf4802016-04-18 13:44:03 -070066 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070067 hostService = srManager.hostService;
68 flowObjectiveService = srManager.flowObjectiveService;
69 }
70
Charles Chandebfea32016-10-24 14:52:01 -070071 protected void init(DeviceId devId) {
Charles Chand9265a32017-06-16 15:19:24 -070072 hostService.getHosts().forEach(host ->
73 host.locations().stream()
74 .filter(location -> location.deviceId().equals(devId))
75 .forEach(location -> processHostAddedAtLocation(host, location))
76 );
Charles Chanc22cef32016-04-29 14:38:22 -070077 }
Charles Chan1eaf4802016-04-18 13:44:03 -070078
Charles Chand9265a32017-06-16 15:19:24 -070079 void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070080 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070081 }
82
Charles Chand9265a32017-06-16 15:19:24 -070083 private void processHostAdded(Host host) {
84 host.locations().forEach(location -> processHostAddedAtLocation(host, location));
Charles Chan1eaf4802016-04-18 13:44:03 -070085 }
86
Charles Chand9265a32017-06-16 15:19:24 -070087 void processHostAddedAtLocation(Host host, HostLocation location) {
88 checkArgument(host.locations().contains(location), "{} is not a location of {}", location, host);
89
Charles Chanceb2a2e2017-09-12 18:57:47 -070090 MacAddress hostMac = host.mac();
91 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -070092 Set<HostLocation> locations = host.locations();
93 Set<IpAddress> ips = host.ipAddresses();
Charles Chanceb2a2e2017-09-12 18:57:47 -070094 log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
Charles Chand9265a32017-06-16 15:19:24 -070095
Charles Chanceb2a2e2017-09-12 18:57:47 -070096 if (srManager.isMasterOf(location)) {
97 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false);
98 ips.forEach(ip ->
99 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
100 );
101 }
102
103 // Use the pair link temporarily before the second location of a dual-homed host shows up.
104 // This do not affect single-homed hosts since the flow will be blocked in
105 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
106 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
107 if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
108 host.locations().stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
109 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
110 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
111 // when the host is untagged
112 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
113
114 processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false);
115 ips.forEach(ip -> processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId,
116 ip, false));
117 });
118 }
119 });
Charles Chand9265a32017-06-16 15:19:24 -0700120 }
121
122 void processHostRemovedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700123 processHostRemoved(event.subject());
124 }
125
Charles Chand9265a32017-06-16 15:19:24 -0700126 private void processHostRemoved(Host host) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700127 MacAddress hostMac = host.mac();
128 VlanId hostVlanId = host.vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700129 Set<HostLocation> locations = host.locations();
Charles Chan41f5ec02016-06-13 18:54:31 -0700130 Set<IpAddress> ips = host.ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700131 log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
Charles Chanc22cef32016-04-29 14:38:22 -0700132
Charles Chan910be6a2017-08-23 14:46:43 -0700133 locations.forEach(location -> {
134 if (srManager.isMasterOf(location)) {
135 processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
136 ips.forEach(ip ->
137 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
138 );
139 }
Charles Chan3ed34d82017-06-22 18:03:14 -0700140
141 // Also remove redirection flows on the pair device if exists.
142 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
143 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(location.deviceId());
Charles Chan910be6a2017-08-23 14:46:43 -0700144 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
145 srManager.mastershipService.isLocalMaster(pairDeviceId.get())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700146 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
147 // when the host is untagged
148 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
149
150 processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
151 ips.forEach(ip ->
152 processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
153 ip, true));
154 }
Charles Chand9265a32017-06-16 15:19:24 -0700155 });
Charles Chanc22cef32016-04-29 14:38:22 -0700156 }
157
Charles Chand9265a32017-06-16 15:19:24 -0700158 void processHostMovedEvent(HostEvent event) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700159 MacAddress hostMac = event.subject().mac();
160 VlanId hostVlanId = event.subject().vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700161 Set<HostLocation> prevLocations = event.prevSubject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700162 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chand9265a32017-06-16 15:19:24 -0700163 Set<HostLocation> newLocations = event.subject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700164 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chan3ed34d82017-06-22 18:03:14 -0700165 log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
Charles Chanc22cef32016-04-29 14:38:22 -0700166
Charles Chand9265a32017-06-16 15:19:24 -0700167 Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
168 .collect(Collectors.toSet());
Charles Chanc22cef32016-04-29 14:38:22 -0700169
Charles Chand9265a32017-06-16 15:19:24 -0700170 // For each old location
Charles Chan910be6a2017-08-23 14:46:43 -0700171 Sets.difference(prevLocations, newLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700172 .forEach(prevLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700173 // Remove routing rules for old IPs
174 Sets.difference(prevIps, newIps).forEach(ip ->
175 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
176 ip, true)
177 );
178
179 // Redirect the flows to pair link if configured
180 // Note: Do not continue removing any rule
181 Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
182 Optional<PortNumber> pairLocalPort = srManager.getPairLocalPorts(prevLocation.deviceId());
183 if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
184 .anyMatch(location -> location.deviceId().equals(pairDeviceId.get()))) {
185 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
186 // when the host is untagged
187 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
188
189 processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
190 newIps.forEach(ip ->
191 processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
192 ip, false));
193 return;
194 }
Charles Chand9265a32017-06-16 15:19:24 -0700195
196 // Remove bridging rule and routing rules for unchanged IPs if the host moves from a switch to another.
197 // Otherwise, do not remove and let the adding part update the old flow
198 if (!newDeviceIds.contains(prevLocation.deviceId())) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700199 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700200 Sets.intersection(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700201 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700202 ip, true)
203 );
204 }
205
206 // Remove bridging rules if new interface vlan is different from old interface vlan
207 // Otherwise, do not remove and let the adding part update the old flow
208 if (newLocations.stream().noneMatch(newLocation -> {
209 VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
210 VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
211 // Host is tagged and the new location has the host vlan in vlan-tagged
Charles Chan3ed34d82017-06-22 18:03:14 -0700212 return srManager.getTaggedVlanId(newLocation).contains(hostVlanId) ||
Charles Chand9265a32017-06-16 15:19:24 -0700213 (oldAssignedVlan != null && newAssignedVlan != null &&
214 // Host is untagged and the new location has the same assigned vlan
215 oldAssignedVlan.equals(newAssignedVlan));
216 })) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700217 processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
Charles Chand9265a32017-06-16 15:19:24 -0700218 }
219
220 // Remove routing rules for unchanged IPs if none of the subnet of new location contains
221 // the IP. Otherwise, do not remove and let the adding part update the old flow
222 Sets.intersection(prevIps, newIps).forEach(ip -> {
223 if (newLocations.stream().noneMatch(newLocation ->
224 srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
Charles Chan3ed34d82017-06-22 18:03:14 -0700225 processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700226 ip, true);
227 }
Charles Chanc22cef32016-04-29 14:38:22 -0700228 });
Charles Chand9265a32017-06-16 15:19:24 -0700229 });
230
231 // For each new location, add all new IPs.
Charles Chan910be6a2017-08-23 14:46:43 -0700232 Sets.difference(newLocations, prevLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700233 .forEach(newLocation -> {
Charles Chan3ed34d82017-06-22 18:03:14 -0700234 processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
Charles Chand9265a32017-06-16 15:19:24 -0700235 newIps.forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700236 processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
Charles Chand9265a32017-06-16 15:19:24 -0700237 ip, false)
238 );
239 });
240
241 // For each unchanged location, add new IPs and remove old IPs.
Charles Chan910be6a2017-08-23 14:46:43 -0700242 Sets.intersection(newLocations, prevLocations).stream().filter(srManager::isMasterOf)
Charles Chanabfe7e02017-08-09 16:50:15 -0700243 .forEach(unchangedLocation -> {
Charles Chand9265a32017-06-16 15:19:24 -0700244 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700245 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
246 hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700247 );
248
249 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chan3ed34d82017-06-22 18:03:14 -0700250 processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(), hostMac,
251 hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700252 );
253 });
Charles Chanc22cef32016-04-29 14:38:22 -0700254 }
255
Charles Chand9265a32017-06-16 15:19:24 -0700256 void processHostUpdatedEvent(HostEvent event) {
Charles Chanb3c1faf2017-11-20 08:46:24 -0800257 MacAddress hostMac = event.subject().mac();
258 VlanId hostVlanId = event.subject().vlan();
Charles Chand9265a32017-06-16 15:19:24 -0700259 Set<HostLocation> locations = event.subject().locations();
Charles Chanc22cef32016-04-29 14:38:22 -0700260 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
Charles Chanc22cef32016-04-29 14:38:22 -0700261 Set<IpAddress> newIps = event.subject().ipAddresses();
Charles Chanb3c1faf2017-11-20 08:46:24 -0800262 log.info("Host {}/{} is updated", hostMac, hostVlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700263
Charles Chan910be6a2017-08-23 14:46:43 -0700264 locations.stream().filter(srManager::isMasterOf).forEach(location -> {
Charles Chand9265a32017-06-16 15:19:24 -0700265 Sets.difference(prevIps, newIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800266 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
Charles Chand9265a32017-06-16 15:19:24 -0700267 );
268 Sets.difference(newIps, prevIps).forEach(ip ->
Charles Chanb3c1faf2017-11-20 08:46:24 -0800269 processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
Charles Chand9265a32017-06-16 15:19:24 -0700270 );
271 });
Charles Chanb3c1faf2017-11-20 08:46:24 -0800272
273 // Use the pair link temporarily before the second location of a dual-homed host shows up.
274 // This do not affect single-homed hosts since the flow will be blocked in
275 // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
276 locations.forEach(location -> {
277 srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
278 if (srManager.mastershipService.isLocalMaster(pairDeviceId) &&
279 locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
280 srManager.getPairLocalPorts(pairDeviceId).ifPresent(pairRemotePort -> {
281 // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
282 // when the host is untagged
283 VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
284
285 Sets.difference(prevIps, newIps).forEach(ip ->
286 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
287 );
288 Sets.difference(newIps, prevIps).forEach(ip ->
289 processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
290 );
291 });
292 }
293 });
294 });
Charles Chanc22cef32016-04-29 14:38:22 -0700295 }
296
297 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800298 * Generates a forwarding objective builder for bridging rules.
299 * <p>
300 * The forwarding objective bridges packets destined to a given MAC to
301 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700302 *
303 * @param deviceId Device that host attaches to
304 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800305 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700306 * @param outport Port that host attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700307 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700308 * @return Forwarding objective builder
309 */
Charles Chand9265a32017-06-16 15:19:24 -0700310 ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800311 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700312 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800313 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
314 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
315 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
316 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700317
Charles Chan90772a72017-02-08 15:52:08 -0800318 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700319 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
320 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700321
Charles Chan90772a72017-02-08 15:52:08 -0800322 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700323 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700324 tbuilder.immediate().setOutput(outport);
325
Charles Chan90772a72017-02-08 15:52:08 -0800326 // Create host meta
327 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
328
329 // Adjust the selector, treatment and meta according to VLAN configuration
330 if (taggedVlans.contains(hostVlanId)) {
331 sbuilder.matchVlanId(hostVlanId);
332 mbuilder.matchVlanId(hostVlanId);
333 } else if (hostVlanId.equals(VlanId.NONE)) {
334 if (untaggedVlan != null) {
335 sbuilder.matchVlanId(untaggedVlan);
336 mbuilder.matchVlanId(untaggedVlan);
337 tbuilder.immediate().popVlan();
338 } else if (nativeVlan != null) {
339 sbuilder.matchVlanId(nativeVlan);
340 mbuilder.matchVlanId(nativeVlan);
341 tbuilder.immediate().popVlan();
342 } else {
Charles Chan184e0242017-05-26 14:23:58 -0700343 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
344 "vlan config", mac, hostVlanId, connectPoint);
345 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800346 }
347 } else {
348 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
349 mac, hostVlanId, connectPoint);
350 return null;
351 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700352
353 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700354 // If the objective is to revoke an existing rule, and for some reason
355 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700356 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700357 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700358 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800359 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700360 return null;
361 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700362
363 return DefaultForwardingObjective.builder()
364 .withFlag(ForwardingObjective.Flag.SPECIFIC)
365 .withSelector(sbuilder.build())
366 .nextStep(portNextObjId)
367 .withPriority(100)
368 .fromApp(srManager.appId)
369 .makePermanent();
370 }
371
Charles Chanc22cef32016-04-29 14:38:22 -0700372 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700373 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
374 * output to given port.
375 *
376 * @param deviceId device ID
377 * @param port port
378 * @param mac mac address
379 * @param vlanId VLAN ID
380 * @param revoke true to revoke the rule; false to populate
381 */
382 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
383 VlanId vlanId, boolean revoke) {
384 log.debug("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
385 mac, vlanId, deviceId, port);
386
387 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, revoke);
388 if (fob == null) {
389 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
390 return;
391 }
392
393 ObjectiveContext context = new DefaultObjectiveContext(
394 (objective) -> log.debug("Brigding rule for {}/{} {}", mac, vlanId,
395 revoke ? "revoked" : "populated"),
396 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
397 revoke ? "revoked" : "populated", mac, vlanId, error));
398 flowObjectiveService.forward(deviceId, revoke ? fob.remove(context) : fob.add(context));
399 }
400
401 /**
402 * Populate or revoke a routing rule on given deviceId that matches given ip,
403 * set destination mac to given mac, set vlan to given vlan and output to given port.
404 *
405 * @param deviceId device ID
406 * @param port port
407 * @param mac mac address
408 * @param vlanId VLAN ID
409 * @param ip IP address
410 * @param revoke true to revoke the rule; false to populate
411 */
412 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
413 VlanId vlanId, IpAddress ip, boolean revoke) {
414 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700415 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
416 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
417 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700418 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700419
Charles Chand9265a32017-06-16 15:19:24 -0700420 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
421 if (revoke) {
Charles Chan910be6a2017-08-23 14:46:43 -0700422 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700423 } else {
Charles Chan910be6a2017-08-23 14:46:43 -0700424 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan370a65b2016-05-10 17:29:47 -0700425 }
Charles Chan370a65b2016-05-10 17:29:47 -0700426 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700427
428 /**
429 * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
430 * and hostMAC connected to given port, and output to given port only when
431 * vlan information is valid.
432 *
433 * @param deviceId device ID that host attaches to
434 * @param portNum port number that host attaches to
435 * @param hostMac mac address of the host connected to the switch port
436 * @param vlanId Vlan ID configured on the switch port
437 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
438 * @param install true to populate the objective, false to revoke
439 */
440 private void updateBridgingRule(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
441 VlanId vlanId, boolean popVlan, boolean install) {
442 // Create host selector
443 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
444 sbuilder.matchEthDst(hostMac);
445
446 // Create host meta
447 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
448
449 sbuilder.matchVlanId(vlanId);
450 mbuilder.matchVlanId(vlanId);
451
452 // Create host treatment
453 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
454 tbuilder.immediate().setOutput(portNum);
455
456 if (popVlan) {
457 tbuilder.immediate().popVlan();
458 }
459
460 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
461 tbuilder.build(), mbuilder.build(), install);
462 if (portNextObjId != -1) {
463 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
464 .withFlag(ForwardingObjective.Flag.SPECIFIC)
465 .withSelector(sbuilder.build())
466 .nextStep(portNextObjId)
467 .withPriority(100)
468 .fromApp(srManager.appId)
469 .makePermanent();
470
471 ObjectiveContext context = new DefaultObjectiveContext(
472 (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
473 install ? "populated" : "revoked"),
474 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
475 install ? "populate" : "revoke", hostMac, vlanId, error));
476 flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
477 } else {
478 log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
479 }
480 }
481
482 /**
483 * Update forwarding objective for unicast bridging and unicast routing.
484 * Also check the validity of updated interface configuration on VLAN.
485 *
486 * @param deviceId device ID that host attaches to
487 * @param portNum port number that host attaches to
488 * @param vlanId Vlan ID configured on the switch port
489 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
490 * @param install true to populate the objective, false to revoke
491 */
492 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
493 boolean popVlan, boolean install) {
494 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
495 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
496
497 if (hosts == null || hosts.size() == 0) {
498 return;
499 }
500
501 hosts.forEach(host -> {
502 MacAddress mac = host.mac();
503 VlanId hostVlanId = host.vlan();
504
505 // Check whether the host vlan is valid for new interface configuration
506 if ((!popVlan && hostVlanId.equals(vlanId)) ||
507 (popVlan && hostVlanId.equals(VlanId.NONE))) {
508 updateBridgingRule(deviceId, portNum, mac, vlanId, popVlan, install);
509 // Update Forwarding objective and corresponding simple Next objective
510 // for each host and IP address connected to given port
511 host.ipAddresses().forEach(ipAddress ->
512 srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
513 mac, vlanId, popVlan, install)
514 );
515 }
516 });
517 }
518
519 /**
520 * Populate or revoke routing rule for each host, according to the updated
521 * subnet configuration on the interface.
522 * @param cp connect point of the updated interface
523 * @param ipPrefixSet IP Prefixes added or removed
524 * @param install true if IP Prefixes added, false otherwise
525 */
526 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
527 Set<Host> hosts = hostService.getConnectedHosts(cp);
528
529 if (hosts == null || hosts.size() == 0) {
530 log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
531 return;
532 }
533
534 // Check whether the host IP address is in the interface's subnet
535 hosts.forEach(host ->
536 host.ipAddresses().forEach(hostIpAddress -> {
537 ipPrefixSet.forEach(ipPrefix -> {
538 if (install && ipPrefix.contains(hostIpAddress)) {
539 srManager.routingRulePopulator.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
540 host.mac(), host.vlan(), cp.port());
541 } else if (!install && ipPrefix.contains(hostIpAddress)) {
542 srManager.routingRulePopulator.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
543 host.mac(), host.vlan(), cp.port());
544 }
545 });
546 }));
547 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700548}