blob: b22979350990f326e034bfa79114b677d18ba23a [file] [log] [blame]
Charles Chand2990362016-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 Chand2990362016-04-18 13:44:03 -070019import org.onlab.packet.IpAddress;
20import org.onlab.packet.MacAddress;
21import org.onlab.packet.VlanId;
Charles Chan59cc16d2017-02-02 16:20:42 -080022import org.onosproject.net.ConnectPoint;
Charles Chand2990362016-04-18 13:44:03 -070023import org.onosproject.net.DeviceId;
Charles Chan6ea94fc2016-05-10 17:29:47 -070024import org.onosproject.net.Host;
Charles Chan93e71ba2016-04-29 14:38:22 -070025import org.onosproject.net.HostLocation;
Charles Chand2990362016-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 Chan6ea94fc2016-05-10 17:29:47 -070038import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chand2990362016-04-18 13:44:03 -070039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Saurav Das018605f2017-02-18 14:05:44 -080042import com.google.common.collect.Sets;
Charles Chand2990362016-04-18 13:44:03 -070043import java.util.Set;
44
Charles Chan59cc16d2017-02-02 16:20:42 -080045import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
46
Charles Chand2990362016-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 Chand2990362016-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 Chand2990362016-04-18 13:44:03 -070063 hostService = srManager.hostService;
64 flowObjectiveService = srManager.flowObjectiveService;
65 }
66
Charles Chan03a73e02016-10-24 14:52:01 -070067 protected void init(DeviceId devId) {
Charles Chand2990362016-04-18 13:44:03 -070068 hostService.getHosts().forEach(host -> {
Saurav Das59232cf2016-04-27 18:35:50 -070069 DeviceId deviceId = host.location().deviceId();
Charles Chan93e71ba2016-04-29 14:38:22 -070070 // The host does not attach to this device
Saurav Das59232cf2016-04-27 18:35:50 -070071 if (!deviceId.equals(devId)) {
Saurav Das59232cf2016-04-27 18:35:50 -070072 return;
73 }
Charles Chan35fd1a72016-06-13 18:54:31 -070074 processHostAdded(host);
Charles Chan93e71ba2016-04-29 14:38:22 -070075 });
76 }
Charles Chand2990362016-04-18 13:44:03 -070077
Charles Chan93e71ba2016-04-29 14:38:22 -070078 protected void processHostAddedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -070079 processHostAdded(event.subject());
Charles Chan93e71ba2016-04-29 14:38:22 -070080 }
81
Charles Chan35fd1a72016-06-13 18:54:31 -070082 protected void processHostAdded(Host host) {
Charles Chan6ea94fc2016-05-10 17:29:47 -070083 MacAddress mac = host.mac();
84 VlanId vlanId = host.vlan();
85 HostLocation location = host.location();
Charles Chan93e71ba2016-04-29 14:38:22 -070086 DeviceId deviceId = location.deviceId();
87 PortNumber port = location.port();
Charles Chan6ea94fc2016-05-10 17:29:47 -070088 Set<IpAddress> ips = host.ipAddresses();
Saurav Das018605f2017-02-18 14:05:44 -080089 log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chan93e71ba2016-04-29 14:38:22 -070090
Charles Chan6ea94fc2016-05-10 17:29:47 -070091 if (accepted(host)) {
Charles Chand2990362016-04-18 13:44:03 -070092 // Populate bridging table entry
Charles Chan93e71ba2016-04-29 14:38:22 -070093 log.debug("Populate L2 table entry for host {} at {}:{}",
94 mac, deviceId, port);
Charles Chand2990362016-04-18 13:44:03 -070095 ForwardingObjective.Builder fob =
Charles Chan93e71ba2016-04-29 14:38:22 -070096 hostFwdObjBuilder(deviceId, mac, vlanId, port);
Saurav Das59232cf2016-04-27 18:35:50 -070097 if (fob == null) {
Charles Chan93e71ba2016-04-29 14:38:22 -070098 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das59232cf2016-04-27 18:35:50 -070099 return;
100 }
Charles Chand2990362016-04-18 13:44:03 -0700101 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan93e71ba2016-04-29 14:38:22 -0700102 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chand2990362016-04-18 13:44:03 -0700103 (objective, error) ->
Charles Chan93e71ba2016-04-29 14:38:22 -0700104 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chand2990362016-04-18 13:44:03 -0700105 flowObjectiveService.forward(deviceId, fob.add(context));
106
Charles Chand2990362016-04-18 13:44:03 -0700107 ips.forEach(ip -> {
Charles Chan93e71ba2016-04-29 14:38:22 -0700108 // Populate IP table entry
Pier Ventre968da122016-12-09 17:26:04 -0800109 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700110 srManager.routingRulePopulator.populateRoute(
111 deviceId, ip.toIpPrefix(), mac, port);
Charles Chand2990362016-04-18 13:44:03 -0700112 }
113 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700114 }
Charles Chand2990362016-04-18 13:44:03 -0700115 }
116
Charles Chan93e71ba2016-04-29 14:38:22 -0700117 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan35fd1a72016-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 Chan93e71ba2016-04-29 14:38:22 -0700125 DeviceId deviceId = location.deviceId();
126 PortNumber port = location.port();
Charles Chan35fd1a72016-06-13 18:54:31 -0700127 Set<IpAddress> ips = host.ipAddresses();
Saurav Das018605f2017-02-18 14:05:44 -0800128 log.info("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
Charles Chan93e71ba2016-04-29 14:38:22 -0700129
Charles Chan35fd1a72016-06-13 18:54:31 -0700130 if (accepted(host)) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700131 // Revoke bridging table entry
132 ForwardingObjective.Builder fob =
133 hostFwdObjBuilder(deviceId, mac, vlanId, port);
134 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 Chan35fd1a72016-06-13 18:54:31 -0700139 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chan93e71ba2016-04-29 14:38:22 -0700140 (objective, error) ->
Charles Chan35fd1a72016-06-13 18:54:31 -0700141 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chan93e71ba2016-04-29 14:38:22 -0700142 flowObjectiveService.forward(deviceId, fob.remove(context));
143
144 // Revoke IP table entry
145 ips.forEach(ip -> {
Pier Ventre968da122016-12-09 17:26:04 -0800146 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700147 srManager.routingRulePopulator.revokeRoute(
148 deviceId, ip.toIpPrefix(), mac, port);
Charles Chan93e71ba2016-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 Das018605f2017-02-18 14:05:44 -0800165 log.info("Host {}/{} is moved from {}:{} to {}:{}",
Charles Chan93e71ba2016-04-29 14:38:22 -0700166 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
167
Charles Chan6ea94fc2016-05-10 17:29:47 -0700168 if (accepted(event.prevSubject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700169 // Revoke previous bridging table entry
170 ForwardingObjective.Builder prevFob =
171 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
172 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 Ventre968da122016-12-09 17:26:04 -0800184 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700185 srManager.routingRulePopulator.revokeRoute(
186 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700187 }
188 });
189 }
190
Charles Chan6ea94fc2016-05-10 17:29:47 -0700191 if (accepted(event.subject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700192 // Populate new bridging table entry
193 ForwardingObjective.Builder newFob =
194 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
195 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 Ventre968da122016-12-09 17:26:04 -0800207 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700208 srManager.routingRulePopulator.populateRoute(
209 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chan93e71ba2016-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 Das018605f2017-02-18 14:05:44 -0800226 log.info("Host {}/{} is updated", mac, vlanId);
Charles Chan93e71ba2016-04-29 14:38:22 -0700227
Charles Chan6ea94fc2016-05-10 17:29:47 -0700228 if (accepted(event.prevSubject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700229 // Revoke previous IP table entry
Saurav Das018605f2017-02-18 14:05:44 -0800230 Sets.difference(prevIps, newIps).forEach(ip -> {
Pier Ventre968da122016-12-09 17:26:04 -0800231 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Saurav Das018605f2017-02-18 14:05:44 -0800232 log.info("revoking previous IP rule:{}", ip);
Charles Chan1cdecff2016-10-27 14:19:48 -0700233 srManager.routingRulePopulator.revokeRoute(
234 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700235 }
236 });
237 }
238
Charles Chan6ea94fc2016-05-10 17:29:47 -0700239 if (accepted(event.subject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700240 // Populate new IP table entry
Saurav Das018605f2017-02-18 14:05:44 -0800241 Sets.difference(newIps, prevIps).forEach(ip -> {
Pier Ventre968da122016-12-09 17:26:04 -0800242 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Saurav Das018605f2017-02-18 14:05:44 -0800243 log.info("populating new IP rule:{}", ip);
Charles Chan1cdecff2016-10-27 14:19:48 -0700244 srManager.routingRulePopulator.populateRoute(
245 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700246 }
247 });
248 }
249 }
250
251 /**
252 * Generates the forwarding objective builder for the host rules.
253 *
254 * @param deviceId Device that host attaches to
255 * @param mac MAC address of the host
256 * @param vlanId VLAN ID of the host
257 * @param outport Port that host attaches to
258 * @return Forwarding objective builder
259 */
260 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chand2990362016-04-18 13:44:03 -0700261 DeviceId deviceId, MacAddress mac, VlanId vlanId,
262 PortNumber outport) {
Charles Chan59cc16d2017-02-02 16:20:42 -0800263 VlanId untaggedVlan = srManager.getUntaggedVlanId(new ConnectPoint(deviceId, outport));
264 VlanId outvlan = (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
Charles Chand2990362016-04-18 13:44:03 -0700265
266 // match rule
267 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
268 sbuilder.matchEthDst(mac);
Charles Chan93e71ba2016-04-29 14:38:22 -0700269 /*
270 * Note: for untagged packets, match on the assigned VLAN.
271 * for tagged packets, match on its incoming VLAN.
272 */
Charles Chand2990362016-04-18 13:44:03 -0700273 if (vlanId.equals(VlanId.NONE)) {
274 sbuilder.matchVlanId(outvlan);
275 } else {
276 sbuilder.matchVlanId(vlanId);
277 }
278
279 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
280 tbuilder.immediate().popVlan();
281 tbuilder.immediate().setOutput(outport);
282
283 // for switch pipelines that need it, provide outgoing vlan as metadata
284 TrafficSelector meta = DefaultTrafficSelector.builder()
285 .matchVlanId(outvlan).build();
286
287 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
288 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
289 tbuilder.build(),
290 meta);
Saurav Das59232cf2016-04-27 18:35:50 -0700291 if (portNextObjId == -1) {
292 // warning log will come from getPortNextObjective method
293 return null;
294 }
Charles Chand2990362016-04-18 13:44:03 -0700295
296 return DefaultForwardingObjective.builder()
297 .withFlag(ForwardingObjective.Flag.SPECIFIC)
298 .withSelector(sbuilder.build())
299 .nextStep(portNextObjId)
300 .withPriority(100)
301 .fromApp(srManager.appId)
302 .makePermanent();
303 }
304
Charles Chan93e71ba2016-04-29 14:38:22 -0700305 /**
Charles Chan03a73e02016-10-24 14:52:01 -0700306 * Determines whether a host should be accepted by SR or not.
Charles Chan6ea94fc2016-05-10 17:29:47 -0700307 *
308 * @param host host to be checked
309 * @return true if segment routing accepts the host
310 */
311 private boolean accepted(Host host) {
Charles Chan6ea94fc2016-05-10 17:29:47 -0700312 SegmentRoutingAppConfig appConfig = srManager.cfgService
313 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chanb3007e12016-05-20 10:55:40 -0700314
315 boolean accepted = appConfig == null ||
316 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
317 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan6ea94fc2016-05-10 17:29:47 -0700318 if (!accepted) {
319 log.info("Ignore suppressed host {}", host.id());
320 }
321 return accepted;
322 }
Charles Chand2990362016-04-18 13:44:03 -0700323}