blob: 82a2829f35dc9d203423f53da6f4b88059317dcb [file] [log] [blame]
Charles Chand2990362016-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 Chand2990362016-04-18 13:44:03 -070023import org.onosproject.net.DeviceId;
Charles Chan6ea94fc2016-05-10 17:29:47 -070024import org.onosproject.net.Host;
Charles Chan93e71ba2016-04-29 14:38:22 -070025import org.onosproject.net.HostLocation;
Charles Chand2990362016-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 Chan6ea94fc2016-05-10 17:29:47 -070038import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chand2990362016-04-18 13:44:03 -070039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
42import java.util.Set;
43
44/**
45 * Handles host-related events.
46 */
47public class HostHandler {
48 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
49 private final SegmentRoutingManager srManager;
Charles Chand2990362016-04-18 13:44:03 -070050 private HostService hostService;
51 private FlowObjectiveService flowObjectiveService;
52
53 /**
54 * Constructs the HostHandler.
55 *
56 * @param srManager Segment Routing manager
57 */
58 public HostHandler(SegmentRoutingManager srManager) {
59 this.srManager = srManager;
Charles Chand2990362016-04-18 13:44:03 -070060 hostService = srManager.hostService;
61 flowObjectiveService = srManager.flowObjectiveService;
62 }
63
Charles Chan03a73e02016-10-24 14:52:01 -070064 protected void init(DeviceId devId) {
Charles Chand2990362016-04-18 13:44:03 -070065 hostService.getHosts().forEach(host -> {
Saurav Das59232cf2016-04-27 18:35:50 -070066 DeviceId deviceId = host.location().deviceId();
Charles Chan93e71ba2016-04-29 14:38:22 -070067 // The host does not attach to this device
Saurav Das59232cf2016-04-27 18:35:50 -070068 if (!deviceId.equals(devId)) {
Saurav Das59232cf2016-04-27 18:35:50 -070069 return;
70 }
Charles Chan35fd1a72016-06-13 18:54:31 -070071 processHostAdded(host);
Charles Chan93e71ba2016-04-29 14:38:22 -070072 });
73 }
Charles Chand2990362016-04-18 13:44:03 -070074
Charles Chan93e71ba2016-04-29 14:38:22 -070075 protected void processHostAddedEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -070076 processHostAdded(event.subject());
Charles Chan93e71ba2016-04-29 14:38:22 -070077 }
78
Charles Chan35fd1a72016-06-13 18:54:31 -070079 protected void processHostAdded(Host host) {
Charles Chan6ea94fc2016-05-10 17:29:47 -070080 MacAddress mac = host.mac();
81 VlanId vlanId = host.vlan();
82 HostLocation location = host.location();
Charles Chan93e71ba2016-04-29 14:38:22 -070083 DeviceId deviceId = location.deviceId();
84 PortNumber port = location.port();
Charles Chan6ea94fc2016-05-10 17:29:47 -070085 Set<IpAddress> ips = host.ipAddresses();
86 log.debug("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chan93e71ba2016-04-29 14:38:22 -070087
Charles Chan6ea94fc2016-05-10 17:29:47 -070088 if (accepted(host)) {
Charles Chand2990362016-04-18 13:44:03 -070089 // Populate bridging table entry
Charles Chan93e71ba2016-04-29 14:38:22 -070090 log.debug("Populate L2 table entry for host {} at {}:{}",
91 mac, deviceId, port);
Charles Chand2990362016-04-18 13:44:03 -070092 ForwardingObjective.Builder fob =
Charles Chan93e71ba2016-04-29 14:38:22 -070093 hostFwdObjBuilder(deviceId, mac, vlanId, port);
Saurav Das59232cf2016-04-27 18:35:50 -070094 if (fob == null) {
Charles Chan93e71ba2016-04-29 14:38:22 -070095 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das59232cf2016-04-27 18:35:50 -070096 return;
97 }
Charles Chand2990362016-04-18 13:44:03 -070098 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chan93e71ba2016-04-29 14:38:22 -070099 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chand2990362016-04-18 13:44:03 -0700100 (objective, error) ->
Charles Chan93e71ba2016-04-29 14:38:22 -0700101 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chand2990362016-04-18 13:44:03 -0700102 flowObjectiveService.forward(deviceId, fob.add(context));
103
Charles Chand2990362016-04-18 13:44:03 -0700104 ips.forEach(ip -> {
Charles Chan93e71ba2016-04-29 14:38:22 -0700105 // Populate IP table entry
Charles Chan03a73e02016-10-24 14:52:01 -0700106 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700107 srManager.routingRulePopulator.populateRoute(
108 deviceId, ip.toIpPrefix(), mac, port);
Charles Chand2990362016-04-18 13:44:03 -0700109 }
110 });
Charles Chan93e71ba2016-04-29 14:38:22 -0700111 }
Charles Chand2990362016-04-18 13:44:03 -0700112 }
113
Charles Chan93e71ba2016-04-29 14:38:22 -0700114 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan35fd1a72016-06-13 18:54:31 -0700115 processHostRemoved(event.subject());
116 }
117
118 protected void processHostRemoved(Host host) {
119 MacAddress mac = host.mac();
120 VlanId vlanId = host.vlan();
121 HostLocation location = host.location();
Charles Chan93e71ba2016-04-29 14:38:22 -0700122 DeviceId deviceId = location.deviceId();
123 PortNumber port = location.port();
Charles Chan35fd1a72016-06-13 18:54:31 -0700124 Set<IpAddress> ips = host.ipAddresses();
Charles Chan93e71ba2016-04-29 14:38:22 -0700125 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
126
Charles Chan35fd1a72016-06-13 18:54:31 -0700127 if (accepted(host)) {
Charles Chan93e71ba2016-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(
Charles Chan35fd1a72016-06-13 18:54:31 -0700136 (objective) -> log.debug("Host rule for {} revoked", host),
Charles Chan93e71ba2016-04-29 14:38:22 -0700137 (objective, error) ->
Charles Chan35fd1a72016-06-13 18:54:31 -0700138 log.warn("Failed to revoke host rule for {}: {}", host, error));
Charles Chan93e71ba2016-04-29 14:38:22 -0700139 flowObjectiveService.forward(deviceId, fob.remove(context));
140
141 // Revoke IP table entry
142 ips.forEach(ip -> {
Charles Chan03a73e02016-10-24 14:52:01 -0700143 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(location, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700144 srManager.routingRulePopulator.revokeRoute(
145 deviceId, ip.toIpPrefix(), mac, port);
Charles Chan93e71ba2016-04-29 14:38:22 -0700146 }
147 });
148 }
149 }
150
151 protected void processHostMovedEvent(HostEvent event) {
152 MacAddress mac = event.subject().mac();
153 VlanId vlanId = event.subject().vlan();
154 HostLocation prevLocation = event.prevSubject().location();
155 DeviceId prevDeviceId = prevLocation.deviceId();
156 PortNumber prevPort = prevLocation.port();
157 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
158 HostLocation newLocation = event.subject().location();
159 DeviceId newDeviceId = newLocation.deviceId();
160 PortNumber newPort = newLocation.port();
161 Set<IpAddress> newIps = event.subject().ipAddresses();
162 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
163 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
164
Charles Chan6ea94fc2016-05-10 17:29:47 -0700165 if (accepted(event.prevSubject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700166 // Revoke previous bridging table entry
167 ForwardingObjective.Builder prevFob =
168 hostFwdObjBuilder(prevDeviceId, mac, vlanId, prevPort);
169 if (prevFob == null) {
170 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
171 return;
172 }
173 ObjectiveContext context = new DefaultObjectiveContext(
174 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
175 (objective, error) ->
176 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
177 flowObjectiveService.forward(prevDeviceId, prevFob.remove(context));
178
179 // Revoke previous IP table entry
180 prevIps.forEach(ip -> {
Charles Chan03a73e02016-10-24 14:52:01 -0700181 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700182 srManager.routingRulePopulator.revokeRoute(
183 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700184 }
185 });
186 }
187
Charles Chan6ea94fc2016-05-10 17:29:47 -0700188 if (accepted(event.subject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700189 // Populate new bridging table entry
190 ForwardingObjective.Builder newFob =
191 hostFwdObjBuilder(newDeviceId, mac, vlanId, newPort);
192 if (newFob == null) {
193 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
194 return;
195 }
196 ObjectiveContext context = new DefaultObjectiveContext(
197 (objective) -> log.debug("Host rule for {} populated", event.subject()),
198 (objective, error) ->
199 log.warn("Failed to populate host rule for {}: {}", event.subject(), error));
200 flowObjectiveService.forward(newDeviceId, newFob.add(context));
201
202 // Populate new IP table entry
203 newIps.forEach(ip -> {
Charles Chan03a73e02016-10-24 14:52:01 -0700204 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700205 srManager.routingRulePopulator.populateRoute(
206 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700207 }
208 });
209 }
210 }
211
212 protected void processHostUpdatedEvent(HostEvent event) {
213 MacAddress mac = event.subject().mac();
214 VlanId vlanId = event.subject().vlan();
215 HostLocation prevLocation = event.prevSubject().location();
216 DeviceId prevDeviceId = prevLocation.deviceId();
217 PortNumber prevPort = prevLocation.port();
218 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
219 HostLocation newLocation = event.subject().location();
220 DeviceId newDeviceId = newLocation.deviceId();
221 PortNumber newPort = newLocation.port();
222 Set<IpAddress> newIps = event.subject().ipAddresses();
223 log.debug("Host {}/{} is updated", mac, vlanId);
224
Charles Chan6ea94fc2016-05-10 17:29:47 -0700225 if (accepted(event.prevSubject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700226 // Revoke previous IP table entry
227 prevIps.forEach(ip -> {
Charles Chan03a73e02016-10-24 14:52:01 -0700228 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(prevLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700229 srManager.routingRulePopulator.revokeRoute(
230 prevDeviceId, ip.toIpPrefix(), mac, prevPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700231 }
232 });
233 }
234
Charles Chan6ea94fc2016-05-10 17:29:47 -0700235 if (accepted(event.subject())) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700236 // Populate new IP table entry
237 newIps.forEach(ip -> {
Charles Chan03a73e02016-10-24 14:52:01 -0700238 if (ip.isIp4() && srManager.deviceConfiguration.inSameSubnet(newLocation, ip)) {
Charles Chan1cdecff2016-10-27 14:19:48 -0700239 srManager.routingRulePopulator.populateRoute(
240 newDeviceId, ip.toIpPrefix(), mac, newPort);
Charles Chan93e71ba2016-04-29 14:38:22 -0700241 }
242 });
243 }
244 }
245
246 /**
247 * Generates the forwarding objective builder for the host rules.
248 *
249 * @param deviceId Device that host attaches to
250 * @param mac MAC address of the host
251 * @param vlanId VLAN ID of the host
252 * @param outport Port that host attaches to
253 * @return Forwarding objective builder
254 */
255 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chand2990362016-04-18 13:44:03 -0700256 DeviceId deviceId, MacAddress mac, VlanId vlanId,
257 PortNumber outport) {
Charles Chan93e71ba2016-04-29 14:38:22 -0700258 // Get assigned VLAN for the subnets
Charles Chand2990362016-04-18 13:44:03 -0700259 VlanId outvlan = null;
260 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outport);
261 if (subnet == null) {
262 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
263 } else {
264 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
265 }
266
267 // match rule
268 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
269 sbuilder.matchEthDst(mac);
Charles Chan93e71ba2016-04-29 14:38:22 -0700270 /*
271 * Note: for untagged packets, match on the assigned VLAN.
272 * for tagged packets, match on its incoming VLAN.
273 */
Charles Chand2990362016-04-18 13:44:03 -0700274 if (vlanId.equals(VlanId.NONE)) {
275 sbuilder.matchVlanId(outvlan);
276 } else {
277 sbuilder.matchVlanId(vlanId);
278 }
279
280 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
281 tbuilder.immediate().popVlan();
282 tbuilder.immediate().setOutput(outport);
283
284 // for switch pipelines that need it, provide outgoing vlan as metadata
285 TrafficSelector meta = DefaultTrafficSelector.builder()
286 .matchVlanId(outvlan).build();
287
288 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
289 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
290 tbuilder.build(),
291 meta);
Saurav Das59232cf2016-04-27 18:35:50 -0700292 if (portNextObjId == -1) {
293 // warning log will come from getPortNextObjective method
294 return null;
295 }
Charles Chand2990362016-04-18 13:44:03 -0700296
297 return DefaultForwardingObjective.builder()
298 .withFlag(ForwardingObjective.Flag.SPECIFIC)
299 .withSelector(sbuilder.build())
300 .nextStep(portNextObjId)
301 .withPriority(100)
302 .fromApp(srManager.appId)
303 .makePermanent();
304 }
305
Charles Chan93e71ba2016-04-29 14:38:22 -0700306 /**
Charles Chan03a73e02016-10-24 14:52:01 -0700307 * Determines whether a host should be accepted by SR or not.
Charles Chan6ea94fc2016-05-10 17:29:47 -0700308 *
309 * @param host host to be checked
310 * @return true if segment routing accepts the host
311 */
312 private boolean accepted(Host host) {
Charles Chan6ea94fc2016-05-10 17:29:47 -0700313 SegmentRoutingAppConfig appConfig = srManager.cfgService
314 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chanb3007e12016-05-20 10:55:40 -0700315
316 boolean accepted = appConfig == null ||
317 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
318 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan6ea94fc2016-05-10 17:29:47 -0700319 if (!accepted) {
320 log.info("Ignore suppressed host {}", host.id());
321 }
322 return accepted;
323 }
Charles Chand2990362016-04-18 13:44:03 -0700324}