blob: 3aa064a23213f9fc6a43b8c5a133adf8e7901bc3 [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
45/**
46 * Handles host-related events.
47 */
48public class HostHandler {
49 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
50 private final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070051 private HostService hostService;
52 private FlowObjectiveService flowObjectiveService;
53
54 /**
55 * Constructs the HostHandler.
56 *
57 * @param srManager Segment Routing manager
58 */
59 public HostHandler(SegmentRoutingManager srManager) {
60 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070061 hostService = srManager.hostService;
62 flowObjectiveService = srManager.flowObjectiveService;
63 }
64
Charles Chandebfea32016-10-24 14:52:01 -070065 protected void init(DeviceId devId) {
Charles Chan1eaf4802016-04-18 13:44:03 -070066 hostService.getHosts().forEach(host -> {
Saurav Das07c74602016-04-27 18:35:50 -070067 DeviceId deviceId = host.location().deviceId();
Charles Chanc22cef32016-04-29 14:38:22 -070068 // The host does not attach to this device
Saurav Das07c74602016-04-27 18:35:50 -070069 if (!deviceId.equals(devId)) {
Saurav Das07c74602016-04-27 18:35:50 -070070 return;
71 }
Charles Chan41f5ec02016-06-13 18:54:31 -070072 processHostAdded(host);
Charles Chanc22cef32016-04-29 14:38:22 -070073 });
74 }
Charles Chan1eaf4802016-04-18 13:44:03 -070075
Charles Chanc22cef32016-04-29 14:38:22 -070076 protected void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070077 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070078 }
79
Charles Chan41f5ec02016-06-13 18:54:31 -070080 protected void processHostAdded(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -070081 MacAddress mac = host.mac();
82 VlanId vlanId = host.vlan();
83 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -070084 DeviceId deviceId = location.deviceId();
85 PortNumber port = location.port();
Charles Chan370a65b2016-05-10 17:29:47 -070086 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -080087 log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -070088
Charles Chan370a65b2016-05-10 17:29:47 -070089 if (accepted(host)) {
Charles Chan1eaf4802016-04-18 13:44:03 -070090 // Populate bridging table entry
Saurav Das368cf212017-03-15 15:15:14 -070091 log.debug("Populating bridging entry for host {}/{} at {}:{}",
92 mac, vlanId, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070093 ForwardingObjective.Builder fob =
Saurav Das2cb38292017-03-29 19:09:17 -070094 bridgingFwdObjBuilder(deviceId, mac, vlanId, port, false);
Saurav Das07c74602016-04-27 18:35:50 -070095 if (fob == null) {
Charles Chanc22cef32016-04-29 14:38:22 -070096 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das07c74602016-04-27 18:35:50 -070097 return;
98 }
Charles Chan1eaf4802016-04-18 13:44:03 -070099 ObjectiveContext context = new DefaultObjectiveContext(
Saurav Das368cf212017-03-15 15:15:14 -0700100 (objective) -> log.debug("Brigding rule for {}/{} populated",
101 mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700102 (objective, error) ->
Saurav Das368cf212017-03-15 15:15:14 -0700103 log.warn("Failed to populate bridging rule for {}/{}: {}",
104 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 =
Saurav Das2cb38292017-03-29 19:09:17 -0700133 bridgingFwdObjBuilder(deviceId, mac, vlanId, port, true);
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 =
Saurav Das2cb38292017-03-29 19:09:17 -0700171 bridgingFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort, true);
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 =
Saurav Das2cb38292017-03-29 19:09:17 -0700194 bridgingFwdObjBuilder(newDeviceId, mac, vlanId, newPort, false);
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
Saurav Das2cb38292017-03-29 19:09:17 -0700261 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700262 * @return Forwarding objective builder
263 */
Charles Chan18fa4252017-02-08 16:10:40 -0800264 private ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800265 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700266 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800267 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
268 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
269 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
270 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700271
Charles Chan90772a72017-02-08 15:52:08 -0800272 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700273 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
274 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700275
Charles Chan90772a72017-02-08 15:52:08 -0800276 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700277 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700278 tbuilder.immediate().setOutput(outport);
279
Charles Chan90772a72017-02-08 15:52:08 -0800280 // Create host meta
281 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
282
283 // Adjust the selector, treatment and meta according to VLAN configuration
284 if (taggedVlans.contains(hostVlanId)) {
285 sbuilder.matchVlanId(hostVlanId);
286 mbuilder.matchVlanId(hostVlanId);
287 } else if (hostVlanId.equals(VlanId.NONE)) {
288 if (untaggedVlan != null) {
289 sbuilder.matchVlanId(untaggedVlan);
290 mbuilder.matchVlanId(untaggedVlan);
291 tbuilder.immediate().popVlan();
292 } else if (nativeVlan != null) {
293 sbuilder.matchVlanId(nativeVlan);
294 mbuilder.matchVlanId(nativeVlan);
295 tbuilder.immediate().popVlan();
296 } else {
Charles Chan184e0242017-05-26 14:23:58 -0700297 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
298 "vlan config", mac, hostVlanId, connectPoint);
299 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800300 }
301 } else {
302 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
303 mac, hostVlanId, connectPoint);
304 return null;
305 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700306
307 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700308 // If the objective is to revoke an existing rule, and for some reason
309 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700310 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700311 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700312 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800313 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700314 return null;
315 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700316
317 return DefaultForwardingObjective.builder()
318 .withFlag(ForwardingObjective.Flag.SPECIFIC)
319 .withSelector(sbuilder.build())
320 .nextStep(portNextObjId)
321 .withPriority(100)
322 .fromApp(srManager.appId)
323 .makePermanent();
324 }
325
Charles Chanc22cef32016-04-29 14:38:22 -0700326 /**
Charles Chandebfea32016-10-24 14:52:01 -0700327 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700328 *
329 * @param host host to be checked
330 * @return true if segment routing accepts the host
331 */
332 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700333 SegmentRoutingAppConfig appConfig = srManager.cfgService
334 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700335
336 boolean accepted = appConfig == null ||
337 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
338 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700339 if (!accepted) {
340 log.info("Ignore suppressed host {}", host.id());
341 }
342 return accepted;
343 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700344}