blob: cf49c5221c7bdc3ba0dcef897602e06741d77c84 [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
Pier Ventre968da122016-12-09 17:26:04 -0800106 if (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 -> {
Pier Ventre968da122016-12-09 17:26:04 -0800143 if (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 -> {
Pier Ventre968da122016-12-09 17:26:04 -0800181 if (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 -> {
Pier Ventre968da122016-12-09 17:26:04 -0800204 if (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 -> {
Pier Ventre968da122016-12-09 17:26:04 -0800228 if (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 -> {
Pier Ventre968da122016-12-09 17:26:04 -0800238 if (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;
Pier Ventre10bd8d12016-11-26 21:05:22 -0800260 // FIXME L2 forwarding should consider also IPv6
261 Ip4Prefix subnet = srManager.deviceConfiguration.getPortIPv4Subnet(deviceId, outport);
Charles Chand2990362016-04-18 13:44:03 -0700262 if (subnet == null) {
263 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
264 } else {
265 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
266 }
267
268 // match rule
269 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
270 sbuilder.matchEthDst(mac);
Charles Chan93e71ba2016-04-29 14:38:22 -0700271 /*
272 * Note: for untagged packets, match on the assigned VLAN.
273 * for tagged packets, match on its incoming VLAN.
274 */
Charles Chand2990362016-04-18 13:44:03 -0700275 if (vlanId.equals(VlanId.NONE)) {
276 sbuilder.matchVlanId(outvlan);
277 } else {
278 sbuilder.matchVlanId(vlanId);
279 }
280
281 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
282 tbuilder.immediate().popVlan();
283 tbuilder.immediate().setOutput(outport);
284
285 // for switch pipelines that need it, provide outgoing vlan as metadata
286 TrafficSelector meta = DefaultTrafficSelector.builder()
287 .matchVlanId(outvlan).build();
288
289 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
290 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
291 tbuilder.build(),
292 meta);
Saurav Das59232cf2016-04-27 18:35:50 -0700293 if (portNextObjId == -1) {
294 // warning log will come from getPortNextObjective method
295 return null;
296 }
Charles Chand2990362016-04-18 13:44:03 -0700297
298 return DefaultForwardingObjective.builder()
299 .withFlag(ForwardingObjective.Flag.SPECIFIC)
300 .withSelector(sbuilder.build())
301 .nextStep(portNextObjId)
302 .withPriority(100)
303 .fromApp(srManager.appId)
304 .makePermanent();
305 }
306
Charles Chan93e71ba2016-04-29 14:38:22 -0700307 /**
Charles Chan03a73e02016-10-24 14:52:01 -0700308 * Determines whether a host should be accepted by SR or not.
Charles Chan6ea94fc2016-05-10 17:29:47 -0700309 *
310 * @param host host to be checked
311 * @return true if segment routing accepts the host
312 */
313 private boolean accepted(Host host) {
Charles Chan6ea94fc2016-05-10 17:29:47 -0700314 SegmentRoutingAppConfig appConfig = srManager.cfgService
315 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chanb3007e12016-05-20 10:55:40 -0700316
317 boolean accepted = appConfig == null ||
318 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
319 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan6ea94fc2016-05-10 17:29:47 -0700320 if (!accepted) {
321 log.info("Ignore suppressed host {}", host.id());
322 }
323 return accepted;
324 }
Charles Chand2990362016-04-18 13:44:03 -0700325}