blob: 62ab1ceae08b8dea45106f1fa95beaeb9f55e4c5 [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
19import org.onlab.packet.Ip4Prefix;
20import org.onlab.packet.IpAddress;
21import org.onlab.packet.MacAddress;
22import org.onlab.packet.VlanId;
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
Charles Chanc22cef32016-04-29 14:38:22 -070091 log.debug("Populate L2 table entry for host {} at {}:{}",
92 mac, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070093 ForwardingObjective.Builder fob =
Charles Chanc22cef32016-04-29 14:38:22 -070094 hostFwdObjBuilder(deviceId, mac, vlanId, port);
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(
Charles Chanc22cef32016-04-29 14:38:22 -0700100 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700101 (objective, error) ->
Charles Chanc22cef32016-04-29 14:38:22 -0700102 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chan1eaf4802016-04-18 13:44:03 -0700103 flowObjectiveService.forward(deviceId, fob.add(context));
104
Charles Chan1eaf4802016-04-18 13:44:03 -0700105 ips.forEach(ip -> {
Charles Chanc22cef32016-04-29 14:38:22 -0700106 // Populate IP table entry
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800107 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700108 srManager.routingRulePopulator.populateRoute(
109 deviceId, ip.toIpPrefix(), mac, port);
Charles Chan1eaf4802016-04-18 13:44:03 -0700110 }
111 });
Charles Chanc22cef32016-04-29 14:38:22 -0700112 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700113 }
114
Charles Chanc22cef32016-04-29 14:38:22 -0700115 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700116 processHostRemoved(event.subject());
117 }
118
119 protected void processHostRemoved(Host host) {
120 MacAddress mac = host.mac();
121 VlanId vlanId = host.vlan();
122 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -0700123 DeviceId deviceId = location.deviceId();
124 PortNumber port = location.port();
Charles Chan41f5ec02016-06-13 18:54:31 -0700125 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800126 log.info("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700127
Charles Chan41f5ec02016-06-13 18:54:31 -0700128 if (accepted(host)) {
Charles Chanc22cef32016-04-29 14:38:22 -0700129 // Revoke bridging table entry
130 ForwardingObjective.Builder fob =
131 hostFwdObjBuilder(deviceId, mac, vlanId, port);
132 if (fob == null) {
133 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
134 return;
135 }
136 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan41f5ec02016-06-13 18:54:31 -0700137 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chanc22cef32016-04-29 14:38:22 -0700138 (objective, error) ->
Charles Chan41f5ec02016-06-13 18:54:31 -0700139 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chanc22cef32016-04-29 14:38:22 -0700140 flowObjectiveService.forward(deviceId, fob.remove(context));
141
142 // Revoke IP table entry
143 ips.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800144 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700145 srManager.routingRulePopulator.revokeRoute(
146 deviceId, ip.toIpPrefix(), mac, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700147 }
148 });
149 }
150 }
151
152 protected void processHostMovedEvent(HostEvent event) {
153 MacAddress mac = event.subject().mac();
154 VlanId vlanId = event.subject().vlan();
155 HostLocation prevLocation = event.prevSubject().location();
156 DeviceId prevDeviceId = prevLocation.deviceId();
157 PortNumber prevPort = prevLocation.port();
158 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
159 HostLocation newLocation = event.subject().location();
160 DeviceId newDeviceId = newLocation.deviceId();
161 PortNumber newPort = newLocation.port();
162 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800163 log.info("Host {}/{} is moved from {}:{} to {}:{}",
Charles Chanc22cef32016-04-29 14:38:22 -0700164 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
165
Charles Chan370a65b2016-05-10 17:29:47 -0700166 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700167 // Revoke previous bridging table entry
168 ForwardingObjective.Builder prevFob =
169 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
170 if (prevFob == null) {
171 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
172 return;
173 }
174 ObjectiveContext context = new DefaultObjectiveContext(
175 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
176 (objective, error) ->
177 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
178 flowObjectiveService.forward(prevDeviceId, prevFob.remove(context));
179
180 // Revoke previous IP table entry
181 prevIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800182 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700183 srManager.routingRulePopulator.revokeRoute(
184 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700185 }
186 });
187 }
188
Charles Chan370a65b2016-05-10 17:29:47 -0700189 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700190 // Populate new bridging table entry
191 ForwardingObjective.Builder newFob =
192 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
193 if (newFob == null) {
194 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
195 return;
196 }
197 ObjectiveContext context = new DefaultObjectiveContext(
198 (objective) -> log.debug("Host rule for {} populated", event.subject()),
199 (objective, error) ->
200 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
201 flowObjectiveService.forward(newDeviceId, newFob.add(context));
202
203 // Populate new IP table entry
204 newIps.forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800205 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chanddac7fd2016-10-27 14:19:48 -0700206 srManager.routingRulePopulator.populateRoute(
207 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700208 }
209 });
210 }
211 }
212
213 protected void processHostUpdatedEvent(HostEvent event) {
214 MacAddress mac = event.subject().mac();
215 VlanId vlanId = event.subject().vlan();
216 HostLocation prevLocation = event.prevSubject().location();
217 DeviceId prevDeviceId = prevLocation.deviceId();
218 PortNumber prevPort = prevLocation.port();
219 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
220 HostLocation newLocation = event.subject().location();
221 DeviceId newDeviceId = newLocation.deviceId();
222 PortNumber newPort = newLocation.port();
223 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800224 log.info("Host {}/{} is updated", mac, vlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700225
Charles Chan370a65b2016-05-10 17:29:47 -0700226 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700227 // Revoke previous IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800228 Sets.difference(prevIps, newIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800229 if (srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800230 log.info("revoking previous IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700231 srManager.routingRulePopulator.revokeRoute(
232 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700233 }
234 });
235 }
236
Charles Chan370a65b2016-05-10 17:29:47 -0700237 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700238 // Populate new IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800239 Sets.difference(newIps, prevIps).forEach(ip -> {
Pier Ventre6b2c1b32016-12-09 17:26:04 -0800240 if (srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Saurav Dasf9332192017-02-18 14:05:44 -0800241 log.info("populating new IP rule:{}", ip);
Charles Chanddac7fd2016-10-27 14:19:48 -0700242 srManager.routingRulePopulator.populateRoute(
243 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chanc22cef32016-04-29 14:38:22 -0700244 }
245 });
246 }
247 }
248
249 /**
250 * Generates the forwarding objective builder for the host rules.
251 *
252 * @param deviceId Device that host attaches to
253 * @param mac MAC address of the host
254 * @param vlanId VLAN ID of the host
255 * @param outport Port that host attaches to
256 * @return Forwarding objective builder
257 */
258 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chan1eaf4802016-04-18 13:44:03 -0700259 DeviceId deviceId, MacAddress mac, VlanId vlanId,
260 PortNumber outport) {
Charles Chanc22cef32016-04-29 14:38:22 -0700261 // Get assigned VLAN for the subnets
Charles Chan1eaf4802016-04-18 13:44:03 -0700262 VlanId outvlan = null;
Pier Ventreb6a7f342016-11-26 21:05:22 -0800263 // FIXME L2 forwarding should consider also IPv6
264 Ip4Prefix subnet = srManager.deviceConfiguration.getPortIPv4Subnet(deviceId, outport);
Charles Chan1eaf4802016-04-18 13:44:03 -0700265 if (subnet == null) {
266 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
267 } else {
268 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
269 }
270
271 // match rule
272 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
273 sbuilder.matchEthDst(mac);
Charles Chanc22cef32016-04-29 14:38:22 -0700274 /*
275 * Note: for untagged packets, match on the assigned VLAN.
276 * for tagged packets, match on its incoming VLAN.
277 */
Charles Chan1eaf4802016-04-18 13:44:03 -0700278 if (vlanId.equals(VlanId.NONE)) {
279 sbuilder.matchVlanId(outvlan);
280 } else {
281 sbuilder.matchVlanId(vlanId);
282 }
283
284 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
285 tbuilder.immediate().popVlan();
286 tbuilder.immediate().setOutput(outport);
287
288 // for switch pipelines that need it, provide outgoing vlan as metadata
289 TrafficSelector meta = DefaultTrafficSelector.builder()
290 .matchVlanId(outvlan).build();
291
292 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
293 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
294 tbuilder.build(),
295 meta);
Saurav Das07c74602016-04-27 18:35:50 -0700296 if (portNextObjId == -1) {
297 // warning log will come from getPortNextObjective method
298 return null;
299 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700300
301 return DefaultForwardingObjective.builder()
302 .withFlag(ForwardingObjective.Flag.SPECIFIC)
303 .withSelector(sbuilder.build())
304 .nextStep(portNextObjId)
305 .withPriority(100)
306 .fromApp(srManager.appId)
307 .makePermanent();
308 }
309
Charles Chanc22cef32016-04-29 14:38:22 -0700310 /**
Charles Chandebfea32016-10-24 14:52:01 -0700311 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700312 *
313 * @param host host to be checked
314 * @return true if segment routing accepts the host
315 */
316 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700317 SegmentRoutingAppConfig appConfig = srManager.cfgService
318 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700319
320 boolean accepted = appConfig == null ||
321 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
322 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700323 if (!accepted) {
324 log.info("Ignore suppressed host {}", host.id());
325 }
326 return accepted;
327 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700328}