blob: 94d880584c3a057e526943563a99789051a981e9 [file] [log] [blame]
Charles Chan1eaf4802016-04-18 13:44:03 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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;
20import org.onlab.packet.MacAddress;
21import org.onlab.packet.VlanId;
Charles Chan10b0fb72017-02-02 16:20:42 -080022import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070023import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070024import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070025import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070026import org.onosproject.net.PortNumber;
27import org.onosproject.net.flow.DefaultTrafficSelector;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.TrafficSelector;
30import org.onosproject.net.flow.TrafficTreatment;
31import org.onosproject.net.flowobjective.DefaultForwardingObjective;
32import org.onosproject.net.flowobjective.DefaultObjectiveContext;
33import org.onosproject.net.flowobjective.FlowObjectiveService;
34import org.onosproject.net.flowobjective.ForwardingObjective;
35import org.onosproject.net.flowobjective.ObjectiveContext;
36import org.onosproject.net.host.HostEvent;
37import org.onosproject.net.host.HostService;
Charles Chan370a65b2016-05-10 17:29:47 -070038import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chan1eaf4802016-04-18 13:44:03 -070039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Saurav Dasf9332192017-02-18 14:05:44 -080042import com.google.common.collect.Sets;
Charles Chan1eaf4802016-04-18 13:44:03 -070043import java.util.Set;
44
Charles Chan10b0fb72017-02-02 16:20:42 -080045import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
46
Charles Chan1eaf4802016-04-18 13:44:03 -070047/**
48 * Handles host-related events.
49 */
50public class HostHandler {
51 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
52 private final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070053 private HostService hostService;
54 private FlowObjectiveService flowObjectiveService;
55
56 /**
57 * Constructs the HostHandler.
58 *
59 * @param srManager Segment Routing manager
60 */
61 public HostHandler(SegmentRoutingManager srManager) {
62 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070063 hostService = srManager.hostService;
64 flowObjectiveService = srManager.flowObjectiveService;
65 }
66
Charles Chandebfea32016-10-24 14:52:01 -070067 protected void init(DeviceId devId) {
Charles Chan1eaf4802016-04-18 13:44:03 -070068 hostService.getHosts().forEach(host -> {
Saurav Das07c74602016-04-27 18:35:50 -070069 DeviceId deviceId = host.location().deviceId();
Charles Chanc22cef32016-04-29 14:38:22 -070070 // The host does not attach to this device
Saurav Das07c74602016-04-27 18:35:50 -070071 if (!deviceId.equals(devId)) {
Saurav Das07c74602016-04-27 18:35:50 -070072 return;
73 }
Charles Chan41f5ec02016-06-13 18:54:31 -070074 processHostAdded(host);
Charles Chanc22cef32016-04-29 14:38:22 -070075 });
76 }
Charles Chan1eaf4802016-04-18 13:44:03 -070077
Charles Chanc22cef32016-04-29 14:38:22 -070078 protected void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070079 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070080 }
81
Charles Chan41f5ec02016-06-13 18:54:31 -070082 protected void processHostAdded(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -070083 MacAddress mac = host.mac();
84 VlanId vlanId = host.vlan();
85 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -070086 DeviceId deviceId = location.deviceId();
87 PortNumber port = location.port();
Charles Chan370a65b2016-05-10 17:29:47 -070088 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -080089 log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -070090
Charles Chan370a65b2016-05-10 17:29:47 -070091 if (accepted(host)) {
Charles Chan1eaf4802016-04-18 13:44:03 -070092 // Populate bridging table entry
Saurav Das368cf212017-03-15 15:15:14 -070093 log.debug("Populating bridging entry for host {}/{} at {}:{}",
94 mac, vlanId, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070095 ForwardingObjective.Builder fob =
Saurav Das2cb38292017-03-29 19:09:17 -070096 bridgingFwdObjBuilder(deviceId, mac, vlanId, port, false);
Saurav Das07c74602016-04-27 18:35:50 -070097 if (fob == null) {
Charles Chanc22cef32016-04-29 14:38:22 -070098 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das07c74602016-04-27 18:35:50 -070099 return;
100 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700101 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das368cf212017-03-15 15:15:14 -0700102 (objective) -> log.debug("Brigding rule for {}/{} populated",
103 mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700104 (objective, error) ->
Saurav Das368cf212017-03-15 15:15:14 -0700105 log.warn("Failed to populate bridging rule for {}/{}: {}",
106 mac, vlanId, error));
Charles Chan1eaf4802016-04-18 13:44:03 -0700107 flowObjectiveService.forward(deviceId, fob.add(context));
108
Charles Chan1eaf4802016-04-18 13:44:03 -0700109 ips.forEach(ip -> {
Charles Chanc22cef32016-04-29 14:38:22 -0700110 // Populate IP table entry
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800111 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700112 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800113 deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -0700114 }
115 });
Charles Chanc22cef32016-04-29 14:38:22 -0700116 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700117 }
118
Charles Chanc22cef32016-04-29 14:38:22 -0700119 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700120 processHostRemoved(event.subject());
121 }
122
123 protected void processHostRemoved(Host host) {
124 MacAddress mac = host.mac();
125 VlanId vlanId = host.vlan();
126 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -0700127 DeviceId deviceId = location.deviceId();
128 PortNumber port = location.port();
Charles Chan41f5ec02016-06-13 18:54:31 -0700129 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800130 log.info("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700131
Charles Chan41f5ec02016-06-13 18:54:31 -0700132 if (accepted(host)) {
Charles Chanc22cef32016-04-29 14:38:22 -0700133 // Revoke bridging table entry
134 ForwardingObjective.Builder fob =
Saurav Das2cb38292017-03-29 19:09:17 -0700135 bridgingFwdObjBuilder(deviceId, mac, vlanId, port, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700136 if (fob == null) {
137 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
138 return;
139 }
140 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan41f5ec02016-06-13 18:54:31 -0700141 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chanc22cef32016-04-29 14:38:22 -0700142 (objective, error) ->
Charles Chan41f5ec02016-06-13 18:54:31 -0700143 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chanc22cef32016-04-29 14:38:22 -0700144 flowObjectiveService.forward(deviceId, fob.remove(context));
145
146 // Revoke IP table entry
147 ips.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800148 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700149 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800150 deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700151 }
152 });
153 }
154 }
155
156 protected void processHostMovedEvent(HostEvent event) {
157 MacAddress mac = event.subject().mac();
158 VlanId vlanId = event.subject().vlan();
159 HostLocation prevLocation = event.prevSubject().location();
160 DeviceId prevDeviceId = prevLocation.deviceId();
161 PortNumber prevPort = prevLocation.port();
162 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
163 HostLocation newLocation = event.subject().location();
164 DeviceId newDeviceId = newLocation.deviceId();
165 PortNumber newPort = newLocation.port();
166 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800167 log.info("Host {}/{} is moved from {}:{} to {}:{}",
Charles Chanc22cef32016-04-29 14:38:22 -0700168 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
169
Charles Chan370a65b2016-05-10 17:29:47 -0700170 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700171 // Revoke previous bridging table entry
172 ForwardingObjective.Builder prevFob =
Saurav Das2cb38292017-03-29 19:09:17 -0700173 bridgingFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700174 if (prevFob == null) {
175 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
176 return;
177 }
178 ObjectiveContext context = new DefaultObjectiveContext(
179 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
180 (objective, error) ->
181 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
182 flowObjectiveService.forward(prevDeviceId, prevFob.remove(context));
183
184 // Revoke previous IP table entry
185 prevIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800186 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700187 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800188 prevDeviceId, ip.toIpPrefix(), mac, vlanId, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700189 }
190 });
191 }
192
Charles Chan370a65b2016-05-10 17:29:47 -0700193 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700194 // Populate new bridging table entry
195 ForwardingObjective.Builder newFob =
Saurav Das2cb38292017-03-29 19:09:17 -0700196 bridgingFwdObjBuilder(newDeviceId, mac, vlanId, newPort, false);
Charles Chanc22cef32016-04-29 14:38:22 -0700197 if (newFob == null) {
198 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
199 return;
200 }
201 ObjectiveContext context = new DefaultObjectiveContext(
202 (objective) -> log.debug("Host rule for {} populated", event.subject()),
203 (objective, error) ->
204 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
205 flowObjectiveService.forward(newDeviceId, newFob.add(context));
206
207 // Populate new IP table entry
208 newIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800209 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700210 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800211 newDeviceId, ip.toIpPrefix(), mac, vlanId, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700212 }
213 });
214 }
215 }
216
217 protected void processHostUpdatedEvent(HostEvent event) {
218 MacAddress mac = event.subject().mac();
219 VlanId vlanId = event.subject().vlan();
220 HostLocation prevLocation = event.prevSubject().location();
221 DeviceId prevDeviceId = prevLocation.deviceId();
222 PortNumber prevPort = prevLocation.port();
223 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
224 HostLocation newLocation = event.subject().location();
225 DeviceId newDeviceId = newLocation.deviceId();
226 PortNumber newPort = newLocation.port();
227 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800228 log.info("Host {}/{} is updated", mac, vlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700229
Charles Chan370a65b2016-05-10 17:29:47 -0700230 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700231 // Revoke previous IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800232 Sets.difference(prevIps, newIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800233 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800234 log.info("revoking previous IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700235 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800236 prevDeviceId, ip.toIpPrefix(), mac, vlanId, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700237 }
238 });
239 }
240
Charles Chan370a65b2016-05-10 17:29:47 -0700241 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700242 // Populate new IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800243 Sets.difference(newIps, prevIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800244 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800245 log.info("populating new IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700246 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800247 newDeviceId, ip.toIpPrefix(), mac, vlanId, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700248 }
249 });
250 }
251 }
252
253 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800254 * Generates a forwarding objective builder for bridging rules.
255 * <p>
256 * The forwarding objective bridges packets destined to a given MAC to
257 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700258 *
259 * @param deviceId Device that host attaches to
260 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800261 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700262 * @param outport Port that host attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700263 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700264 * @return Forwarding objective builder
265 */
Charles Chan18fa4252017-02-08 16:10:40 -0800266 private ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800267 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700268 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800269 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
270 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
271 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
272 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700273
Charles Chan90772a72017-02-08 15:52:08 -0800274 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700275 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
276 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700277
Charles Chan90772a72017-02-08 15:52:08 -0800278 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700279 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700280 tbuilder.immediate().setOutput(outport);
281
Charles Chan90772a72017-02-08 15:52:08 -0800282 // Create host meta
283 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
284
285 // Adjust the selector, treatment and meta according to VLAN configuration
286 if (taggedVlans.contains(hostVlanId)) {
287 sbuilder.matchVlanId(hostVlanId);
288 mbuilder.matchVlanId(hostVlanId);
289 } else if (hostVlanId.equals(VlanId.NONE)) {
290 if (untaggedVlan != null) {
291 sbuilder.matchVlanId(untaggedVlan);
292 mbuilder.matchVlanId(untaggedVlan);
293 tbuilder.immediate().popVlan();
294 } else if (nativeVlan != null) {
295 sbuilder.matchVlanId(nativeVlan);
296 mbuilder.matchVlanId(nativeVlan);
297 tbuilder.immediate().popVlan();
298 } else {
299 // TODO: This check is turned off for now since vRouter still assumes that
300 // hosts are internally tagged with INTERNAL_VLAN.
301 // We should turn this back on when we move forward to the bridging CPR approach.
302 //
303 //log.warn("Untagged host {}/{} is not allowed on {} without untagged or native vlan",
304 // mac, hostVlanId, connectPoint);
305 //return null;
306 sbuilder.matchVlanId(INTERNAL_VLAN);
307 mbuilder.matchVlanId(INTERNAL_VLAN);
308 tbuilder.immediate().popVlan();
309 }
310 } else {
311 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
312 mac, hostVlanId, connectPoint);
313 return null;
314 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700315
316 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700317 // If the objective is to revoke an existing rule, and for some reason
318 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700319 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700320 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700321 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800322 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700323 return null;
324 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700325
326 return DefaultForwardingObjective.builder()
327 .withFlag(ForwardingObjective.Flag.SPECIFIC)
328 .withSelector(sbuilder.build())
329 .nextStep(portNextObjId)
330 .withPriority(100)
331 .fromApp(srManager.appId)
332 .makePermanent();
333 }
334
Charles Chanc22cef32016-04-29 14:38:22 -0700335 /**
Charles Chandebfea32016-10-24 14:52:01 -0700336 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700337 *
338 * @param host host to be checked
339 * @return true if segment routing accepts the host
340 */
341 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700342 SegmentRoutingAppConfig appConfig = srManager.cfgService
343 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700344
345 boolean accepted = appConfig == null ||
346 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
347 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700348 if (!accepted) {
349 log.info("Ignore suppressed host {}", host.id());
350 }
351 return accepted;
352 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700353}