blob: d5c93fd6083046c7a82f008c0764cefe078a6131 [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 Chan370a65b2016-05-10 17:29:47 -070074 processHostAddedEventInternal(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 Chan370a65b2016-05-10 17:29:47 -070079 processHostAddedEventInternal(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070080 }
81
Charles Chan370a65b2016-05-10 17:29:47 -070082 private void processHostAddedEventInternal(Host host) {
83 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) {
119 MacAddress mac = event.subject().mac();
120 VlanId vlanId = event.subject().vlan();
121 HostLocation location = event.subject().location();
122 DeviceId deviceId = location.deviceId();
123 PortNumber port = location.port();
124 Set<IpAddress> ips = event.subject().ipAddresses();
125 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
126
Charles Chan370a65b2016-05-10 17:29:47 -0700127 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700128 // Revoke bridging table entry
129 ForwardingObjective.Builder fob =
130 hostFwdObjBuilder(deviceId, mac, vlanId, port);
131 if (fob == null) {
132 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
133 return;
134 }
135 ObjectiveContext context = new DefaultObjectiveContext(
136 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
137 (objective, error) ->
138 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
139 flowObjectiveService.forward(deviceId, fob.remove(context));
140
141 // Revoke IP table entry
142 ips.forEach(ip -> {
143 if (ip.isIp4()) {
144 removePerHostRoute(location, ip.getIp4Address());
145 srManager.routingRulePopulator.revokeIpRuleForHost(
146 deviceId, ip.getIp4Address(), mac, port);
147 }
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();
163 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
164 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 -> {
182 if (ip.isIp4()) {
183 removePerHostRoute(prevLocation, ip.getIp4Address());
184 srManager.routingRulePopulator.revokeIpRuleForHost(
185 prevDeviceId, ip.getIp4Address(), mac, prevPort);
186 }
187 });
188 }
189
Charles Chan370a65b2016-05-10 17:29:47 -0700190 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700191 // Populate new bridging table entry
192 ForwardingObjective.Builder newFob =
193 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
194 if (newFob == null) {
195 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
196 return;
197 }
198 ObjectiveContext context = new DefaultObjectiveContext(
199 (objective) -> log.debug("Host rule for {} populated", event.subject()),
200 (objective, error) ->
201 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
202 flowObjectiveService.forward(newDeviceId, newFob.add(context));
203
204 // Populate new IP table entry
205 newIps.forEach(ip -> {
206 if (ip.isIp4()) {
207 addPerHostRoute(newLocation, ip.getIp4Address());
208 srManager.routingRulePopulator.populateIpRuleForHost(
209 newDeviceId, ip.getIp4Address(), mac, newPort);
210 }
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();
226 log.debug("Host {}/{} is updated", mac, vlanId);
227
Charles Chan370a65b2016-05-10 17:29:47 -0700228 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700229 // Revoke previous IP table entry
230 prevIps.forEach(ip -> {
231 if (ip.isIp4()) {
232 removePerHostRoute(prevLocation, ip.getIp4Address());
233 srManager.routingRulePopulator.revokeIpRuleForHost(
234 prevDeviceId, ip.getIp4Address(), mac, prevPort);
235 }
236 });
237 }
238
Charles Chan370a65b2016-05-10 17:29:47 -0700239 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700240 // Populate new IP table entry
241 newIps.forEach(ip -> {
242 if (ip.isIp4()) {
243 addPerHostRoute(newLocation, ip.getIp4Address());
244 srManager.routingRulePopulator.populateIpRuleForHost(
245 newDeviceId, ip.getIp4Address(), mac, newPort);
246 }
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 Chan1eaf4802016-04-18 13:44:03 -0700261 DeviceId deviceId, MacAddress mac, VlanId vlanId,
262 PortNumber outport) {
Charles Chanc22cef32016-04-29 14:38:22 -0700263 // Get assigned VLAN for the subnets
Charles Chan1eaf4802016-04-18 13:44:03 -0700264 VlanId outvlan = null;
265 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outport);
266 if (subnet == null) {
267 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
268 } else {
269 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
270 }
271
272 // match rule
273 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
274 sbuilder.matchEthDst(mac);
Charles Chanc22cef32016-04-29 14:38:22 -0700275 /*
276 * Note: for untagged packets, match on the assigned VLAN.
277 * for tagged packets, match on its incoming VLAN.
278 */
Charles Chan1eaf4802016-04-18 13:44:03 -0700279 if (vlanId.equals(VlanId.NONE)) {
280 sbuilder.matchVlanId(outvlan);
281 } else {
282 sbuilder.matchVlanId(vlanId);
283 }
284
285 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
286 tbuilder.immediate().popVlan();
287 tbuilder.immediate().setOutput(outport);
288
289 // for switch pipelines that need it, provide outgoing vlan as metadata
290 TrafficSelector meta = DefaultTrafficSelector.builder()
291 .matchVlanId(outvlan).build();
292
293 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
294 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
295 tbuilder.build(),
296 meta);
Saurav Das07c74602016-04-27 18:35:50 -0700297 if (portNextObjId == -1) {
298 // warning log will come from getPortNextObjective method
299 return null;
300 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700301
302 return DefaultForwardingObjective.builder()
303 .withFlag(ForwardingObjective.Flag.SPECIFIC)
304 .withSelector(sbuilder.build())
305 .nextStep(portNextObjId)
306 .withPriority(100)
307 .fromApp(srManager.appId)
308 .makePermanent();
309 }
310
Charles Chanc22cef32016-04-29 14:38:22 -0700311 /**
312 * Add per host route to subnet list and populate the flow rule if the host
313 * does not belong to the configured subnet.
314 *
315 * @param location location of the host being added
316 * @param ip IP address of the host being added
317 */
318 private void addPerHostRoute(ConnectPoint location, Ip4Address ip) {
319 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
320 location.deviceId(), location.port());
321 if (portSubnet != null && !portSubnet.contains(ip)) {
322 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
323 srManager.deviceConfiguration.addSubnet(location, ip4Prefix);
324 srManager.defaultRoutingHandler.populateSubnet(location,
325 ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700326 }
327 }
328
Charles Chanc22cef32016-04-29 14:38:22 -0700329 /**
330 * Remove per host route from subnet list and revoke the flow rule if the
331 * host does not belong to the configured subnet.
332 *
333 * @param location location of the host being removed
334 * @param ip IP address of the host being removed
335 */
336 private void removePerHostRoute(ConnectPoint location, Ip4Address ip) {
337 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
338 location.deviceId(), location.port());
339 if (portSubnet != null && !portSubnet.contains(ip)) {
340 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
341 srManager.deviceConfiguration.removeSubnet(location, ip4Prefix);
342 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700343 }
344 }
Charles Chan370a65b2016-05-10 17:29:47 -0700345
346 /**
347 * Check if a host is accepted or not.
348 *
349 * @param host host to be checked
350 * @return true if segment routing accepts the host
351 */
352 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700353 SegmentRoutingAppConfig appConfig = srManager.cfgService
354 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700355
356 boolean accepted = appConfig == null ||
357 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
358 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700359 if (!accepted) {
360 log.info("Ignore suppressed host {}", host.id());
361 }
362 return accepted;
363 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700364}