blob: 12d6d78a1e6619fe05d8036b209bc3e9a3d45ab6 [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
Charles Chanc22cef32016-04-29 14:38:22 -070093 log.debug("Populate L2 table entry for host {} at {}:{}",
94 mac, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070095 ForwardingObjective.Builder fob =
Charles Chan18fa4252017-02-08 16:10:40 -080096 bridgingFwdObjBuilder(deviceId, mac, vlanId, port);
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(
Charles Chanc22cef32016-04-29 14:38:22 -0700102 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700103 (objective, error) ->
Charles Chanc22cef32016-04-29 14:38:22 -0700104 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chan1eaf4802016-04-18 13:44:03 -0700105 flowObjectiveService.forward(deviceId, fob.add(context));
106
Charles Chan1eaf4802016-04-18 13:44:03 -0700107 ips.forEach(ip -> {
Charles Chanc22cef32016-04-29 14:38:22 -0700108 // Populate IP table entry
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800109 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700110 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800111 deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -0700112 }
113 });
Charles Chanc22cef32016-04-29 14:38:22 -0700114 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700115 }
116
Charles Chanc22cef32016-04-29 14:38:22 -0700117 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700118 processHostRemoved(event.subject());
119 }
120
121 protected void processHostRemoved(Host host) {
122 MacAddress mac = host.mac();
123 VlanId vlanId = host.vlan();
124 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -0700125 DeviceId deviceId = location.deviceId();
126 PortNumber port = location.port();
Charles Chan41f5ec02016-06-13 18:54:31 -0700127 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800128 log.info("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700129
Charles Chan41f5ec02016-06-13 18:54:31 -0700130 if (accepted(host)) {
Charles Chanc22cef32016-04-29 14:38:22 -0700131 // Revoke bridging table entry
132 ForwardingObjective.Builder fob =
Charles Chan18fa4252017-02-08 16:10:40 -0800133 bridgingFwdObjBuilder(deviceId, mac, vlanId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700134 if (fob == null) {
135 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
136 return;
137 }
138 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan41f5ec02016-06-13 18:54:31 -0700139 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chanc22cef32016-04-29 14:38:22 -0700140 (objective, error) ->
Charles Chan41f5ec02016-06-13 18:54:31 -0700141 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chanc22cef32016-04-29 14:38:22 -0700142 flowObjectiveService.forward(deviceId, fob.remove(context));
143
144 // Revoke IP table entry
145 ips.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800146 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700147 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800148 deviceId, ip.toIpPrefix(), mac, vlanId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700149 }
150 });
151 }
152 }
153
154 protected void processHostMovedEvent(HostEvent event) {
155 MacAddress mac = event.subject().mac();
156 VlanId vlanId = event.subject().vlan();
157 HostLocation prevLocation = event.prevSubject().location();
158 DeviceId prevDeviceId = prevLocation.deviceId();
159 PortNumber prevPort = prevLocation.port();
160 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
161 HostLocation newLocation = event.subject().location();
162 DeviceId newDeviceId = newLocation.deviceId();
163 PortNumber newPort = newLocation.port();
164 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800165 log.info("Host {}/{} is moved from {}:{} to {}:{}",
Charles Chanc22cef32016-04-29 14:38:22 -0700166 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
167
Charles Chan370a65b2016-05-10 17:29:47 -0700168 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700169 // Revoke previous bridging table entry
170 ForwardingObjective.Builder prevFob =
Charles Chan18fa4252017-02-08 16:10:40 -0800171 bridgingFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700172 if (prevFob == null) {
173 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
174 return;
175 }
176 ObjectiveContext context = new DefaultObjectiveContext(
177 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
178 (objective, error) ->
179 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
180 flowObjectiveService.forward(prevDeviceId, prevFob.remove(context));
181
182 // Revoke previous IP table entry
183 prevIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800184 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700185 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800186 prevDeviceId, ip.toIpPrefix(), mac, vlanId, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700187 }
188 });
189 }
190
Charles Chan370a65b2016-05-10 17:29:47 -0700191 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700192 // Populate new bridging table entry
193 ForwardingObjective.Builder newFob =
Charles Chan18fa4252017-02-08 16:10:40 -0800194 bridgingFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700195 if (newFob == null) {
196 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
197 return;
198 }
199 ObjectiveContext context = new DefaultObjectiveContext(
200 (objective) -> log.debug("Host rule for {} populated", event.subject()),
201 (objective, error) ->
202 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
203 flowObjectiveService.forward(newDeviceId, newFob.add(context));
204
205 // Populate new IP table entry
206 newIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800207 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700208 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800209 newDeviceId, ip.toIpPrefix(), mac, vlanId, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700210 }
211 });
212 }
213 }
214
215 protected void processHostUpdatedEvent(HostEvent event) {
216 MacAddress mac = event.subject().mac();
217 VlanId vlanId = event.subject().vlan();
218 HostLocation prevLocation = event.prevSubject().location();
219 DeviceId prevDeviceId = prevLocation.deviceId();
220 PortNumber prevPort = prevLocation.port();
221 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
222 HostLocation newLocation = event.subject().location();
223 DeviceId newDeviceId = newLocation.deviceId();
224 PortNumber newPort = newLocation.port();
225 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800226 log.info("Host {}/{} is updated", mac, vlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700227
Charles Chan370a65b2016-05-10 17:29:47 -0700228 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700229 // Revoke previous IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800230 Sets.difference(prevIps, newIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800231 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800232 log.info("revoking previous IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700233 srManager.routingRulePopulator.revokeRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800234 prevDeviceId, ip.toIpPrefix(), mac, vlanId, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700235 }
236 });
237 }
238
Charles Chan370a65b2016-05-10 17:29:47 -0700239 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700240 // Populate new IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800241 Sets.difference(newIps, prevIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800242 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800243 log.info("populating new IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700244 srManager.routingRulePopulator.populateRoute(
Charles Chan90772a72017-02-08 15:52:08 -0800245 newDeviceId, ip.toIpPrefix(), mac, vlanId, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700246 }
247 });
248 }
249 }
250
251 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800252 * Generates a forwarding objective builder for bridging rules.
253 * <p>
254 * The forwarding objective bridges packets destined to a given MAC to
255 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700256 *
257 * @param deviceId Device that host attaches to
258 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800259 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700260 * @param outport Port that host attaches to
261 * @return Forwarding objective builder
262 */
Charles Chan18fa4252017-02-08 16:10:40 -0800263 private ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800264 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Charles Chan1eaf4802016-04-18 13:44:03 -0700265 PortNumber outport) {
Charles Chan90772a72017-02-08 15:52:08 -0800266 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
267 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
268 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
269 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700270
Charles Chan90772a72017-02-08 15:52:08 -0800271 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700272 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
273 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700274
Charles Chan90772a72017-02-08 15:52:08 -0800275 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700276 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700277 tbuilder.immediate().setOutput(outport);
278
Charles Chan90772a72017-02-08 15:52:08 -0800279 // Create host meta
280 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
281
282 // Adjust the selector, treatment and meta according to VLAN configuration
283 if (taggedVlans.contains(hostVlanId)) {
284 sbuilder.matchVlanId(hostVlanId);
285 mbuilder.matchVlanId(hostVlanId);
286 } else if (hostVlanId.equals(VlanId.NONE)) {
287 if (untaggedVlan != null) {
288 sbuilder.matchVlanId(untaggedVlan);
289 mbuilder.matchVlanId(untaggedVlan);
290 tbuilder.immediate().popVlan();
291 } else if (nativeVlan != null) {
292 sbuilder.matchVlanId(nativeVlan);
293 mbuilder.matchVlanId(nativeVlan);
294 tbuilder.immediate().popVlan();
295 } else {
296 // TODO: This check is turned off for now since vRouter still assumes that
297 // hosts are internally tagged with INTERNAL_VLAN.
298 // We should turn this back on when we move forward to the bridging CPR approach.
299 //
300 //log.warn("Untagged host {}/{} is not allowed on {} without untagged or native vlan",
301 // mac, hostVlanId, connectPoint);
302 //return null;
303 sbuilder.matchVlanId(INTERNAL_VLAN);
304 mbuilder.matchVlanId(INTERNAL_VLAN);
305 tbuilder.immediate().popVlan();
306 }
307 } else {
308 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
309 mac, hostVlanId, connectPoint);
310 return null;
311 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700312
313 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
314 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
315 tbuilder.build(),
Charles Chan90772a72017-02-08 15:52:08 -0800316 mbuilder.build());
Saurav Das07c74602016-04-27 18:35:50 -0700317 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800318 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700319 return null;
320 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700321
322 return DefaultForwardingObjective.builder()
323 .withFlag(ForwardingObjective.Flag.SPECIFIC)
324 .withSelector(sbuilder.build())
325 .nextStep(portNextObjId)
326 .withPriority(100)
327 .fromApp(srManager.appId)
328 .makePermanent();
329 }
330
Charles Chanc22cef32016-04-29 14:38:22 -0700331 /**
Charles Chandebfea32016-10-24 14:52:01 -0700332 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700333 *
334 * @param host host to be checked
335 * @return true if segment routing accepts the host
336 */
337 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700338 SegmentRoutingAppConfig appConfig = srManager.cfgService
339 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700340
341 boolean accepted = appConfig == null ||
342 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
343 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700344 if (!accepted) {
345 log.info("Ignore suppressed host {}", host.id());
346 }
347 return accepted;
348 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700349}