blob: c82b675fd67cba37a34fbf826d4344ab21ee7523 [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 Chanc22cef32016-04-29 14:38:22 -070019import com.google.common.collect.ImmutableSet;
20import org.onlab.packet.Ip4Address;
Charles Chan1eaf4802016-04-18 13:44:03 -070021import org.onlab.packet.Ip4Prefix;
22import org.onlab.packet.IpAddress;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.VlanId;
Charles Chan1eaf4802016-04-18 13:44:03 -070025import org.onosproject.net.ConnectPoint;
26import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070027import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070028import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070029import org.onosproject.net.PortNumber;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.TrafficSelector;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.onosproject.net.flowobjective.DefaultForwardingObjective;
35import org.onosproject.net.flowobjective.DefaultObjectiveContext;
36import org.onosproject.net.flowobjective.FlowObjectiveService;
37import org.onosproject.net.flowobjective.ForwardingObjective;
38import org.onosproject.net.flowobjective.ObjectiveContext;
39import org.onosproject.net.host.HostEvent;
40import org.onosproject.net.host.HostService;
Charles Chan370a65b2016-05-10 17:29:47 -070041import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chan1eaf4802016-04-18 13:44:03 -070042import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
45import java.util.Set;
46
47/**
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
Saurav Das07c74602016-04-27 18:35:50 -070067 protected void readInitialHosts(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();
89 log.debug("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 Chanc22cef32016-04-29 14:38:22 -070096 hostFwdObjBuilder(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
Charles Chan1eaf4802016-04-18 13:44:03 -0700109 if (ip.isIp4()) {
Charles Chanc22cef32016-04-29 14:38:22 -0700110 addPerHostRoute(location, ip.getIp4Address());
Charles Chan1eaf4802016-04-18 13:44:03 -0700111 srManager.routingRulePopulator.populateIpRuleForHost(
112 deviceId, ip.getIp4Address(), mac, port);
113 }
114 });
Charles Chanc22cef32016-04-29 14:38:22 -0700115 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700116 }
117
Charles Chanc22cef32016-04-29 14:38:22 -0700118 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -0700119 processHostRemoved(event.subject());
120 }
121
122 protected void processHostRemoved(Host host) {
123 MacAddress mac = host.mac();
124 VlanId vlanId = host.vlan();
125 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -0700126 DeviceId deviceId = location.deviceId();
127 PortNumber port = location.port();
Charles Chan41f5ec02016-06-13 18:54:31 -0700128 Set<IpAddress> ips = host.ipAddresses();
Charles Chanc22cef32016-04-29 14:38:22 -0700129 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
130
Charles Chan41f5ec02016-06-13 18:54:31 -0700131 if (accepted(host)) {
Charles Chanc22cef32016-04-29 14:38:22 -0700132 // Revoke bridging table entry
133 ForwardingObjective.Builder fob =
134 hostFwdObjBuilder(deviceId, mac, vlanId, port);
135 if (fob == null) {
136 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
137 return;
138 }
139 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan41f5ec02016-06-13 18:54:31 -0700140 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chanc22cef32016-04-29 14:38:22 -0700141 (objective, error) ->
Charles Chan41f5ec02016-06-13 18:54:31 -0700142 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chanc22cef32016-04-29 14:38:22 -0700143 flowObjectiveService.forward(deviceId, fob.remove(context));
144
145 // Revoke IP table entry
146 ips.forEach(ip -> {
147 if (ip.isIp4()) {
148 removePerHostRoute(location, ip.getIp4Address());
149 srManager.routingRulePopulator.revokeIpRuleForHost(
150 deviceId, ip.getIp4Address(), mac, port);
151 }
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();
167 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
168 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 =
173 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
174 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 -> {
186 if (ip.isIp4()) {
187 removePerHostRoute(prevLocation, ip.getIp4Address());
188 srManager.routingRulePopulator.revokeIpRuleForHost(
189 prevDeviceId, ip.getIp4Address(), mac, prevPort);
190 }
191 });
192 }
193
Charles Chan370a65b2016-05-10 17:29:47 -0700194 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700195 // Populate new bridging table entry
196 ForwardingObjective.Builder newFob =
197 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
198 if (newFob == null) {
199 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
200 return;
201 }
202 ObjectiveContext context = new DefaultObjectiveContext(
203 (objective) -> log.debug("Host rule for {} populated", event.subject()),
204 (objective, error) ->
205 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
206 flowObjectiveService.forward(newDeviceId, newFob.add(context));
207
208 // Populate new IP table entry
209 newIps.forEach(ip -> {
210 if (ip.isIp4()) {
211 addPerHostRoute(newLocation, ip.getIp4Address());
212 srManager.routingRulePopulator.populateIpRuleForHost(
213 newDeviceId, ip.getIp4Address(), mac, newPort);
214 }
215 });
216 }
217 }
218
219 protected void processHostUpdatedEvent(HostEvent event) {
220 MacAddress mac = event.subject().mac();
221 VlanId vlanId = event.subject().vlan();
222 HostLocation prevLocation = event.prevSubject().location();
223 DeviceId prevDeviceId = prevLocation.deviceId();
224 PortNumber prevPort = prevLocation.port();
225 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
226 HostLocation newLocation = event.subject().location();
227 DeviceId newDeviceId = newLocation.deviceId();
228 PortNumber newPort = newLocation.port();
229 Set<IpAddress> newIps = event.subject().ipAddresses();
230 log.debug("Host {}/{} is updated", mac, vlanId);
231
Charles Chan370a65b2016-05-10 17:29:47 -0700232 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700233 // Revoke previous IP table entry
234 prevIps.forEach(ip -> {
235 if (ip.isIp4()) {
236 removePerHostRoute(prevLocation, ip.getIp4Address());
237 srManager.routingRulePopulator.revokeIpRuleForHost(
238 prevDeviceId, ip.getIp4Address(), mac, prevPort);
239 }
240 });
241 }
242
Charles Chan370a65b2016-05-10 17:29:47 -0700243 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700244 // Populate new IP table entry
245 newIps.forEach(ip -> {
246 if (ip.isIp4()) {
247 addPerHostRoute(newLocation, ip.getIp4Address());
248 srManager.routingRulePopulator.populateIpRuleForHost(
249 newDeviceId, ip.getIp4Address(), mac, newPort);
250 }
251 });
252 }
253 }
254
255 /**
256 * Generates the forwarding objective builder for the host rules.
257 *
258 * @param deviceId Device that host attaches to
259 * @param mac MAC address of the host
260 * @param vlanId VLAN ID of the host
261 * @param outport Port that host attaches to
262 * @return Forwarding objective builder
263 */
264 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chan1eaf4802016-04-18 13:44:03 -0700265 DeviceId deviceId, MacAddress mac, VlanId vlanId,
266 PortNumber outport) {
Charles Chanc22cef32016-04-29 14:38:22 -0700267 // Get assigned VLAN for the subnets
Charles Chan1eaf4802016-04-18 13:44:03 -0700268 VlanId outvlan = null;
269 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outport);
270 if (subnet == null) {
271 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
272 } else {
273 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
274 }
275
276 // match rule
277 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
278 sbuilder.matchEthDst(mac);
Charles Chanc22cef32016-04-29 14:38:22 -0700279 /*
280 * Note: for untagged packets, match on the assigned VLAN.
281 * for tagged packets, match on its incoming VLAN.
282 */
Charles Chan1eaf4802016-04-18 13:44:03 -0700283 if (vlanId.equals(VlanId.NONE)) {
284 sbuilder.matchVlanId(outvlan);
285 } else {
286 sbuilder.matchVlanId(vlanId);
287 }
288
289 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
290 tbuilder.immediate().popVlan();
291 tbuilder.immediate().setOutput(outport);
292
293 // for switch pipelines that need it, provide outgoing vlan as metadata
294 TrafficSelector meta = DefaultTrafficSelector.builder()
295 .matchVlanId(outvlan).build();
296
297 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
298 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
299 tbuilder.build(),
300 meta);
Saurav Das07c74602016-04-27 18:35:50 -0700301 if (portNextObjId == -1) {
302 // warning log will come from getPortNextObjective method
303 return null;
304 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700305
306 return DefaultForwardingObjective.builder()
307 .withFlag(ForwardingObjective.Flag.SPECIFIC)
308 .withSelector(sbuilder.build())
309 .nextStep(portNextObjId)
310 .withPriority(100)
311 .fromApp(srManager.appId)
312 .makePermanent();
313 }
314
Charles Chanc22cef32016-04-29 14:38:22 -0700315 /**
316 * Add per host route to subnet list and populate the flow rule if the host
317 * does not belong to the configured subnet.
318 *
319 * @param location location of the host being added
320 * @param ip IP address of the host being added
321 */
322 private void addPerHostRoute(ConnectPoint location, Ip4Address ip) {
323 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
324 location.deviceId(), location.port());
325 if (portSubnet != null && !portSubnet.contains(ip)) {
326 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
327 srManager.deviceConfiguration.addSubnet(location, ip4Prefix);
328 srManager.defaultRoutingHandler.populateSubnet(location,
329 ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700330 }
331 }
332
Charles Chanc22cef32016-04-29 14:38:22 -0700333 /**
334 * Remove per host route from subnet list and revoke the flow rule if the
335 * host does not belong to the configured subnet.
336 *
337 * @param location location of the host being removed
338 * @param ip IP address of the host being removed
339 */
340 private void removePerHostRoute(ConnectPoint location, Ip4Address ip) {
341 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
342 location.deviceId(), location.port());
343 if (portSubnet != null && !portSubnet.contains(ip)) {
344 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
345 srManager.deviceConfiguration.removeSubnet(location, ip4Prefix);
346 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700347 }
348 }
Charles Chan370a65b2016-05-10 17:29:47 -0700349
350 /**
351 * Check if a host is accepted or not.
352 *
353 * @param host host to be checked
354 * @return true if segment routing accepts the host
355 */
356 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700357 SegmentRoutingAppConfig appConfig = srManager.cfgService
358 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700359
360 boolean accepted = appConfig == null ||
361 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
362 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700363 if (!accepted) {
364 log.info("Ignore suppressed host {}", host.id());
365 }
366 return accepted;
367 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700368}