blob: 07f6d8dd6dda51451cd3d9b7f9ae330ec5cbc5ab [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 Chanc22cef32016-04-29 14:38:22 -0700257 MacAddress mac = event.subject().mac();
258 VlanId vlanId = 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();
Saurav Dasf9332192017-02-18 14:05:44 -0800262 log.info("Host {}/{} is updated", mac, vlanId);
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 ->
266 processRoutingRule(location.deviceId(), location.port(), mac, vlanId, ip, true)
267 );
268 Sets.difference(newIps, prevIps).forEach(ip ->
269 processRoutingRule(location.deviceId(), location.port(), mac, vlanId, ip, false)
270 );
271 });
Charles Chanc22cef32016-04-29 14:38:22 -0700272 }
273
274 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800275 * Generates a forwarding objective builder for bridging rules.
276 * <p>
277 * The forwarding objective bridges packets destined to a given MAC to
278 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700279 *
280 * @param deviceId Device that host attaches to
281 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800282 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700283 * @param outport Port that host attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700284 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700285 * @return Forwarding objective builder
286 */
Charles Chand9265a32017-06-16 15:19:24 -0700287 ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800288 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700289 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800290 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
291 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
292 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
293 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700294
Charles Chan90772a72017-02-08 15:52:08 -0800295 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700296 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
297 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700298
Charles Chan90772a72017-02-08 15:52:08 -0800299 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700300 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700301 tbuilder.immediate().setOutput(outport);
302
Charles Chan90772a72017-02-08 15:52:08 -0800303 // Create host meta
304 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
305
306 // Adjust the selector, treatment and meta according to VLAN configuration
307 if (taggedVlans.contains(hostVlanId)) {
308 sbuilder.matchVlanId(hostVlanId);
309 mbuilder.matchVlanId(hostVlanId);
310 } else if (hostVlanId.equals(VlanId.NONE)) {
311 if (untaggedVlan != null) {
312 sbuilder.matchVlanId(untaggedVlan);
313 mbuilder.matchVlanId(untaggedVlan);
314 tbuilder.immediate().popVlan();
315 } else if (nativeVlan != null) {
316 sbuilder.matchVlanId(nativeVlan);
317 mbuilder.matchVlanId(nativeVlan);
318 tbuilder.immediate().popVlan();
319 } else {
Charles Chan184e0242017-05-26 14:23:58 -0700320 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
321 "vlan config", mac, hostVlanId, connectPoint);
322 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800323 }
324 } else {
325 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
326 mac, hostVlanId, connectPoint);
327 return null;
328 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700329
330 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700331 // If the objective is to revoke an existing rule, and for some reason
332 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700333 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700334 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700335 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800336 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700337 return null;
338 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700339
340 return DefaultForwardingObjective.builder()
341 .withFlag(ForwardingObjective.Flag.SPECIFIC)
342 .withSelector(sbuilder.build())
343 .nextStep(portNextObjId)
344 .withPriority(100)
345 .fromApp(srManager.appId)
346 .makePermanent();
347 }
348
Charles Chanc22cef32016-04-29 14:38:22 -0700349 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700350 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
351 * output to given port.
352 *
353 * @param deviceId device ID
354 * @param port port
355 * @param mac mac address
356 * @param vlanId VLAN ID
357 * @param revoke true to revoke the rule; false to populate
358 */
359 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
360 VlanId vlanId, boolean revoke) {
361 log.debug("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
362 mac, vlanId, deviceId, port);
363
364 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, revoke);
365 if (fob == null) {
366 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
367 return;
368 }
369
370 ObjectiveContext context = new DefaultObjectiveContext(
371 (objective) -> log.debug("Brigding rule for {}/{} {}", mac, vlanId,
372 revoke ? "revoked" : "populated"),
373 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
374 revoke ? "revoked" : "populated", mac, vlanId, error));
375 flowObjectiveService.forward(deviceId, revoke ? fob.remove(context) : fob.add(context));
376 }
377
378 /**
379 * Populate or revoke a routing rule on given deviceId that matches given ip,
380 * set destination mac to given mac, set vlan to given vlan and output to given port.
381 *
382 * @param deviceId device ID
383 * @param port port
384 * @param mac mac address
385 * @param vlanId VLAN ID
386 * @param ip IP address
387 * @param revoke true to revoke the rule; false to populate
388 */
389 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
390 VlanId vlanId, IpAddress ip, boolean revoke) {
391 ConnectPoint location = new ConnectPoint(deviceId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700392 if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
393 log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
394 return;
Charles Chan9595e6a2017-06-15 19:25:25 -0700395 }
Charles Chan9595e6a2017-06-15 19:25:25 -0700396
Charles Chand9265a32017-06-16 15:19:24 -0700397 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
398 if (revoke) {
Charles Chan910be6a2017-08-23 14:46:43 -0700399 srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chand9265a32017-06-16 15:19:24 -0700400 } else {
Charles Chan910be6a2017-08-23 14:46:43 -0700401 srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan370a65b2016-05-10 17:29:47 -0700402 }
Charles Chan370a65b2016-05-10 17:29:47 -0700403 }
Jonghwan Hyune5ef7622017-08-25 17:48:36 -0700404
405 /**
406 * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
407 * and hostMAC connected to given port, and output to given port only when
408 * vlan information is valid.
409 *
410 * @param deviceId device ID that host attaches to
411 * @param portNum port number that host attaches to
412 * @param hostMac mac address of the host connected to the switch port
413 * @param vlanId Vlan ID configured on the switch port
414 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
415 * @param install true to populate the objective, false to revoke
416 */
417 private void updateBridgingRule(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
418 VlanId vlanId, boolean popVlan, boolean install) {
419 // Create host selector
420 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
421 sbuilder.matchEthDst(hostMac);
422
423 // Create host meta
424 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
425
426 sbuilder.matchVlanId(vlanId);
427 mbuilder.matchVlanId(vlanId);
428
429 // Create host treatment
430 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
431 tbuilder.immediate().setOutput(portNum);
432
433 if (popVlan) {
434 tbuilder.immediate().popVlan();
435 }
436
437 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
438 tbuilder.build(), mbuilder.build(), install);
439 if (portNextObjId != -1) {
440 ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
441 .withFlag(ForwardingObjective.Flag.SPECIFIC)
442 .withSelector(sbuilder.build())
443 .nextStep(portNextObjId)
444 .withPriority(100)
445 .fromApp(srManager.appId)
446 .makePermanent();
447
448 ObjectiveContext context = new DefaultObjectiveContext(
449 (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
450 install ? "populated" : "revoked"),
451 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
452 install ? "populate" : "revoke", hostMac, vlanId, error));
453 flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
454 } else {
455 log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
456 }
457 }
458
459 /**
460 * Update forwarding objective for unicast bridging and unicast routing.
461 * Also check the validity of updated interface configuration on VLAN.
462 *
463 * @param deviceId device ID that host attaches to
464 * @param portNum port number that host attaches to
465 * @param vlanId Vlan ID configured on the switch port
466 * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
467 * @param install true to populate the objective, false to revoke
468 */
469 void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
470 boolean popVlan, boolean install) {
471 ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
472 Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
473
474 if (hosts == null || hosts.size() == 0) {
475 return;
476 }
477
478 hosts.forEach(host -> {
479 MacAddress mac = host.mac();
480 VlanId hostVlanId = host.vlan();
481
482 // Check whether the host vlan is valid for new interface configuration
483 if ((!popVlan && hostVlanId.equals(vlanId)) ||
484 (popVlan && hostVlanId.equals(VlanId.NONE))) {
485 updateBridgingRule(deviceId, portNum, mac, vlanId, popVlan, install);
486 // Update Forwarding objective and corresponding simple Next objective
487 // for each host and IP address connected to given port
488 host.ipAddresses().forEach(ipAddress ->
489 srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
490 mac, vlanId, popVlan, install)
491 );
492 }
493 });
494 }
495
496 /**
497 * Populate or revoke routing rule for each host, according to the updated
498 * subnet configuration on the interface.
499 * @param cp connect point of the updated interface
500 * @param ipPrefixSet IP Prefixes added or removed
501 * @param install true if IP Prefixes added, false otherwise
502 */
503 void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
504 Set<Host> hosts = hostService.getConnectedHosts(cp);
505
506 if (hosts == null || hosts.size() == 0) {
507 log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
508 return;
509 }
510
511 // Check whether the host IP address is in the interface's subnet
512 hosts.forEach(host ->
513 host.ipAddresses().forEach(hostIpAddress -> {
514 ipPrefixSet.forEach(ipPrefix -> {
515 if (install && ipPrefix.contains(hostIpAddress)) {
516 srManager.routingRulePopulator.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
517 host.mac(), host.vlan(), cp.port());
518 } else if (!install && ipPrefix.contains(hostIpAddress)) {
519 srManager.routingRulePopulator.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
520 host.mac(), host.vlan(), cp.port());
521 }
522 });
523 }));
524 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700525}