blob: 441597ee56399e8377ed8015cfe8b658930e3903 [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 =
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(
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 =
Charles Chan18fa4252017-02-08 16:10:40 -0800135 bridgingFwdObjBuilder(deviceId, mac, vlanId, port);
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 =
Charles Chan18fa4252017-02-08 16:10:40 -0800173 bridgingFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
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 =
Charles Chan18fa4252017-02-08 16:10:40 -0800196 bridgingFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
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
263 * @return Forwarding objective builder
264 */
Charles Chan18fa4252017-02-08 16:10:40 -0800265 private ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800266 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Charles Chan1eaf4802016-04-18 13:44:03 -0700267 PortNumber outport) {
Charles Chan90772a72017-02-08 15:52:08 -0800268 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
269 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
270 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
271 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700272
Charles Chan90772a72017-02-08 15:52:08 -0800273 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700274 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
275 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700276
Charles Chan90772a72017-02-08 15:52:08 -0800277 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700278 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700279 tbuilder.immediate().setOutput(outport);
280
Charles Chan90772a72017-02-08 15:52:08 -0800281 // Create host meta
282 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
283
284 // Adjust the selector, treatment and meta according to VLAN configuration
285 if (taggedVlans.contains(hostVlanId)) {
286 sbuilder.matchVlanId(hostVlanId);
287 mbuilder.matchVlanId(hostVlanId);
288 } else if (hostVlanId.equals(VlanId.NONE)) {
289 if (untaggedVlan != null) {
290 sbuilder.matchVlanId(untaggedVlan);
291 mbuilder.matchVlanId(untaggedVlan);
292 tbuilder.immediate().popVlan();
293 } else if (nativeVlan != null) {
294 sbuilder.matchVlanId(nativeVlan);
295 mbuilder.matchVlanId(nativeVlan);
296 tbuilder.immediate().popVlan();
297 } else {
298 // TODO: This check is turned off for now since vRouter still assumes that
299 // hosts are internally tagged with INTERNAL_VLAN.
300 // We should turn this back on when we move forward to the bridging CPR approach.
301 //
302 //log.warn("Untagged host {}/{} is not allowed on {} without untagged or native vlan",
303 // mac, hostVlanId, connectPoint);
304 //return null;
305 sbuilder.matchVlanId(INTERNAL_VLAN);
306 mbuilder.matchVlanId(INTERNAL_VLAN);
307 tbuilder.immediate().popVlan();
308 }
309 } else {
310 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
311 mac, hostVlanId, connectPoint);
312 return null;
313 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700314
315 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
316 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
317 tbuilder.build(),
Charles Chan90772a72017-02-08 15:52:08 -0800318 mbuilder.build());
Saurav Das07c74602016-04-27 18:35:50 -0700319 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800320 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700321 return null;
322 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700323
324 return DefaultForwardingObjective.builder()
325 .withFlag(ForwardingObjective.Flag.SPECIFIC)
326 .withSelector(sbuilder.build())
327 .nextStep(portNextObjId)
328 .withPriority(100)
329 .fromApp(srManager.appId)
330 .makePermanent();
331 }
332
Charles Chanc22cef32016-04-29 14:38:22 -0700333 /**
Charles Chandebfea32016-10-24 14:52:01 -0700334 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700335 *
336 * @param host host to be checked
337 * @return true if segment routing accepts the host
338 */
339 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700340 SegmentRoutingAppConfig appConfig = srManager.cfgService
341 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700342
343 boolean accepted = appConfig == null ||
344 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
345 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700346 if (!accepted) {
347 log.info("Ignore suppressed host {}", host.id());
348 }
349 return accepted;
350 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700351}