blob: 132bff56324dee3865bc7de6c86f82316b688cc8 [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.provider.netcfghost.NetworkConfigHostProvider;
42import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chan1eaf4802016-04-18 13:44:03 -070043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
46import java.util.Set;
47
48/**
49 * Handles host-related events.
50 */
51public class HostHandler {
52 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
53 private final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070054 private HostService hostService;
55 private FlowObjectiveService flowObjectiveService;
56
57 /**
58 * Constructs the HostHandler.
59 *
60 * @param srManager Segment Routing manager
61 */
62 public HostHandler(SegmentRoutingManager srManager) {
63 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070064 hostService = srManager.hostService;
65 flowObjectiveService = srManager.flowObjectiveService;
66 }
67
Saurav Das07c74602016-04-27 18:35:50 -070068 protected void readInitialHosts(DeviceId devId) {
Charles Chan1eaf4802016-04-18 13:44:03 -070069 hostService.getHosts().forEach(host -> {
Saurav Das07c74602016-04-27 18:35:50 -070070 DeviceId deviceId = host.location().deviceId();
Charles Chanc22cef32016-04-29 14:38:22 -070071 // The host does not attach to this device
Saurav Das07c74602016-04-27 18:35:50 -070072 if (!deviceId.equals(devId)) {
Saurav Das07c74602016-04-27 18:35:50 -070073 return;
74 }
Charles Chan370a65b2016-05-10 17:29:47 -070075 processHostAddedEventInternal(host);
Charles Chanc22cef32016-04-29 14:38:22 -070076 });
77 }
Charles Chan1eaf4802016-04-18 13:44:03 -070078
Charles Chanc22cef32016-04-29 14:38:22 -070079 protected void processHostAddedEvent(HostEvent event) {
Charles Chan370a65b2016-05-10 17:29:47 -070080 processHostAddedEventInternal(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070081 }
82
Charles Chan370a65b2016-05-10 17:29:47 -070083 private void processHostAddedEventInternal(Host host) {
84 MacAddress mac = host.mac();
85 VlanId vlanId = host.vlan();
86 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -070087 DeviceId deviceId = location.deviceId();
88 PortNumber port = location.port();
Charles Chan370a65b2016-05-10 17:29:47 -070089 Set<IpAddress> ips = host.ipAddresses();
90 log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -070091
Charles Chan370a65b2016-05-10 17:29:47 -070092 if (accepted(host)) {
Charles Chan1eaf4802016-04-18 13:44:03 -070093 // Populate bridging table entry
Charles Chanc22cef32016-04-29 14:38:22 -070094 log.debug("Populate L2 table entry for host {} at {}:{}",
95 mac, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070096 ForwardingObjective.Builder fob =
Charles Chanc22cef32016-04-29 14:38:22 -070097 hostFwdObjBuilder(deviceId, mac, vlanId, port);
Saurav Das07c74602016-04-27 18:35:50 -070098 if (fob == null) {
Charles Chanc22cef32016-04-29 14:38:22 -070099 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das07c74602016-04-27 18:35:50 -0700100 return;
101 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700102 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chanc22cef32016-04-29 14:38:22 -0700103 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700104 (objective, error) ->
Charles Chanc22cef32016-04-29 14:38:22 -0700105 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chan1eaf4802016-04-18 13:44:03 -0700106 flowObjectiveService.forward(deviceId, fob.add(context));
107
Charles Chan1eaf4802016-04-18 13:44:03 -0700108 ips.forEach(ip -> {
Charles Chanc22cef32016-04-29 14:38:22 -0700109 // Populate IP table entry
Charles Chan1eaf4802016-04-18 13:44:03 -0700110 if (ip.isIp4()) {
Charles Chanc22cef32016-04-29 14:38:22 -0700111 addPerHostRoute(location, ip.getIp4Address());
Charles Chan1eaf4802016-04-18 13:44:03 -0700112 srManager.routingRulePopulator.populateIpRuleForHost(
113 deviceId, ip.getIp4Address(), mac, port);
114 }
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) {
120 MacAddress mac = event.subject().mac();
121 VlanId vlanId = event.subject().vlan();
122 HostLocation location = event.subject().location();
123 DeviceId deviceId = location.deviceId();
124 PortNumber port = location.port();
125 Set<IpAddress> ips = event.subject().ipAddresses();
126 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
127
Charles Chan370a65b2016-05-10 17:29:47 -0700128 if (accepted(event.subject())) {
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(
137 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
138 (objective, error) ->
139 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
140 flowObjectiveService.forward(deviceId, fob.remove(context));
141
142 // Revoke IP table entry
143 ips.forEach(ip -> {
144 if (ip.isIp4()) {
145 removePerHostRoute(location, ip.getIp4Address());
146 srManager.routingRulePopulator.revokeIpRuleForHost(
147 deviceId, ip.getIp4Address(), mac, port);
148 }
149 });
150 }
151 }
152
153 protected void processHostMovedEvent(HostEvent event) {
154 MacAddress mac = event.subject().mac();
155 VlanId vlanId = event.subject().vlan();
156 HostLocation prevLocation = event.prevSubject().location();
157 DeviceId prevDeviceId = prevLocation.deviceId();
158 PortNumber prevPort = prevLocation.port();
159 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
160 HostLocation newLocation = event.subject().location();
161 DeviceId newDeviceId = newLocation.deviceId();
162 PortNumber newPort = newLocation.port();
163 Set<IpAddress> newIps = event.subject().ipAddresses();
164 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
165 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
166
Charles Chan370a65b2016-05-10 17:29:47 -0700167 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700168 // Revoke previous bridging table entry
169 ForwardingObjective.Builder prevFob =
170 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
171 if (prevFob == null) {
172 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
173 return;
174 }
175 ObjectiveContext context = new DefaultObjectiveContext(
176 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
177 (objective, error) ->
178 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
179 flowObjectiveService.forward(prevDeviceId, prevFob.remove(context));
180
181 // Revoke previous IP table entry
182 prevIps.forEach(ip -> {
183 if (ip.isIp4()) {
184 removePerHostRoute(prevLocation, ip.getIp4Address());
185 srManager.routingRulePopulator.revokeIpRuleForHost(
186 prevDeviceId, ip.getIp4Address(), mac, prevPort);
187 }
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 =
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 -> {
207 if (ip.isIp4()) {
208 addPerHostRoute(newLocation, ip.getIp4Address());
209 srManager.routingRulePopulator.populateIpRuleForHost(
210 newDeviceId, ip.getIp4Address(), mac, newPort);
211 }
212 });
213 }
214 }
215
216 protected void processHostUpdatedEvent(HostEvent event) {
217 MacAddress mac = event.subject().mac();
218 VlanId vlanId = event.subject().vlan();
219 HostLocation prevLocation = event.prevSubject().location();
220 DeviceId prevDeviceId = prevLocation.deviceId();
221 PortNumber prevPort = prevLocation.port();
222 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
223 HostLocation newLocation = event.subject().location();
224 DeviceId newDeviceId = newLocation.deviceId();
225 PortNumber newPort = newLocation.port();
226 Set<IpAddress> newIps = event.subject().ipAddresses();
227 log.debug("Host {}/{} is updated", mac, vlanId);
228
Charles Chan370a65b2016-05-10 17:29:47 -0700229 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700230 // Revoke previous IP table entry
231 prevIps.forEach(ip -> {
232 if (ip.isIp4()) {
233 removePerHostRoute(prevLocation, ip.getIp4Address());
234 srManager.routingRulePopulator.revokeIpRuleForHost(
235 prevDeviceId, ip.getIp4Address(), mac, prevPort);
236 }
237 });
238 }
239
Charles Chan370a65b2016-05-10 17:29:47 -0700240 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700241 // Populate new IP table entry
242 newIps.forEach(ip -> {
243 if (ip.isIp4()) {
244 addPerHostRoute(newLocation, ip.getIp4Address());
245 srManager.routingRulePopulator.populateIpRuleForHost(
246 newDeviceId, ip.getIp4Address(), mac, newPort);
247 }
248 });
249 }
250 }
251
252 /**
253 * Generates the forwarding objective builder for the host rules.
254 *
255 * @param deviceId Device that host attaches to
256 * @param mac MAC address of the host
257 * @param vlanId VLAN ID of the host
258 * @param outport Port that host attaches to
259 * @return Forwarding objective builder
260 */
261 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chan1eaf4802016-04-18 13:44:03 -0700262 DeviceId deviceId, MacAddress mac, VlanId vlanId,
263 PortNumber outport) {
Charles Chanc22cef32016-04-29 14:38:22 -0700264 // Get assigned VLAN for the subnets
Charles Chan1eaf4802016-04-18 13:44:03 -0700265 VlanId outvlan = null;
266 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outport);
267 if (subnet == null) {
268 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
269 } else {
270 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
271 }
272
273 // match rule
274 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
275 sbuilder.matchEthDst(mac);
Charles Chanc22cef32016-04-29 14:38:22 -0700276 /*
277 * Note: for untagged packets, match on the assigned VLAN.
278 * for tagged packets, match on its incoming VLAN.
279 */
Charles Chan1eaf4802016-04-18 13:44:03 -0700280 if (vlanId.equals(VlanId.NONE)) {
281 sbuilder.matchVlanId(outvlan);
282 } else {
283 sbuilder.matchVlanId(vlanId);
284 }
285
286 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
287 tbuilder.immediate().popVlan();
288 tbuilder.immediate().setOutput(outport);
289
290 // for switch pipelines that need it, provide outgoing vlan as metadata
291 TrafficSelector meta = DefaultTrafficSelector.builder()
292 .matchVlanId(outvlan).build();
293
294 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
295 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
296 tbuilder.build(),
297 meta);
Saurav Das07c74602016-04-27 18:35:50 -0700298 if (portNextObjId == -1) {
299 // warning log will come from getPortNextObjective method
300 return null;
301 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700302
303 return DefaultForwardingObjective.builder()
304 .withFlag(ForwardingObjective.Flag.SPECIFIC)
305 .withSelector(sbuilder.build())
306 .nextStep(portNextObjId)
307 .withPriority(100)
308 .fromApp(srManager.appId)
309 .makePermanent();
310 }
311
Charles Chanc22cef32016-04-29 14:38:22 -0700312 /**
313 * Add per host route to subnet list and populate the flow rule if the host
314 * does not belong to the configured subnet.
315 *
316 * @param location location of the host being added
317 * @param ip IP address of the host being added
318 */
319 private void addPerHostRoute(ConnectPoint location, Ip4Address ip) {
320 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
321 location.deviceId(), location.port());
322 if (portSubnet != null && !portSubnet.contains(ip)) {
323 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
324 srManager.deviceConfiguration.addSubnet(location, ip4Prefix);
325 srManager.defaultRoutingHandler.populateSubnet(location,
326 ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700327 }
328 }
329
Charles Chanc22cef32016-04-29 14:38:22 -0700330 /**
331 * Remove per host route from subnet list and revoke the flow rule if the
332 * host does not belong to the configured subnet.
333 *
334 * @param location location of the host being removed
335 * @param ip IP address of the host being removed
336 */
337 private void removePerHostRoute(ConnectPoint location, Ip4Address ip) {
338 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
339 location.deviceId(), location.port());
340 if (portSubnet != null && !portSubnet.contains(ip)) {
341 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
342 srManager.deviceConfiguration.removeSubnet(location, ip4Prefix);
343 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700344 }
345 }
Charles Chan370a65b2016-05-10 17:29:47 -0700346
347 /**
348 * Check if a host is accepted or not.
349 *
350 * @param host host to be checked
351 * @return true if segment routing accepts the host
352 */
353 private boolean accepted(Host host) {
354 // Always accept configured hosts
355 if (host.providerId().equals(NetworkConfigHostProvider.PROVIDER_ID)) {
356 return true;
357 }
358
359 SegmentRoutingAppConfig appConfig = srManager.cfgService
360 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
361 boolean accepted = appConfig != null &&
362 appConfig.hostLearning() &&
363 !appConfig.suppressHost().contains(host.location());
364 if (!accepted) {
365 log.info("Ignore suppressed host {}", host.id());
366 }
367 return accepted;
368 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700369}