blob: 6709efd659f1322735927da27e1fa5199c974a23 [file] [log] [blame]
Charles Chan1eaf4802016-04-18 13:44:03 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chan1eaf4802016-04-18 13:44:03 -07003 *
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 Chan1eaf4802016-04-18 13:44:03 -070019import org.onlab.packet.IpAddress;
20import org.onlab.packet.MacAddress;
21import org.onlab.packet.VlanId;
Charles Chan10b0fb72017-02-02 16:20:42 -080022import org.onosproject.net.ConnectPoint;
Charles Chan1eaf4802016-04-18 13:44:03 -070023import org.onosproject.net.DeviceId;
Charles Chan370a65b2016-05-10 17:29:47 -070024import org.onosproject.net.Host;
Charles Chanc22cef32016-04-29 14:38:22 -070025import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-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 Chan370a65b2016-05-10 17:29:47 -070038import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
Charles Chan1eaf4802016-04-18 13:44:03 -070039import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Saurav Dasf9332192017-02-18 14:05:44 -080042import com.google.common.collect.Sets;
Charles Chan1eaf4802016-04-18 13:44:03 -070043import java.util.Set;
44
45/**
46 * Handles host-related events.
47 */
48public class HostHandler {
49 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
Charles Chan114aec72017-06-19 14:00:53 -070050 protected final SegmentRoutingManager srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070051 private HostService hostService;
52 private FlowObjectiveService flowObjectiveService;
53
54 /**
55 * Constructs the HostHandler.
56 *
57 * @param srManager Segment Routing manager
58 */
59 public HostHandler(SegmentRoutingManager srManager) {
60 this.srManager = srManager;
Charles Chan1eaf4802016-04-18 13:44:03 -070061 hostService = srManager.hostService;
62 flowObjectiveService = srManager.flowObjectiveService;
63 }
64
Charles Chandebfea32016-10-24 14:52:01 -070065 protected void init(DeviceId devId) {
Charles Chan1eaf4802016-04-18 13:44:03 -070066 hostService.getHosts().forEach(host -> {
Saurav Das07c74602016-04-27 18:35:50 -070067 DeviceId deviceId = host.location().deviceId();
Charles Chanc22cef32016-04-29 14:38:22 -070068 // The host does not attach to this device
Saurav Das07c74602016-04-27 18:35:50 -070069 if (!deviceId.equals(devId)) {
Saurav Das07c74602016-04-27 18:35:50 -070070 return;
71 }
Charles Chan41f5ec02016-06-13 18:54:31 -070072 processHostAdded(host);
Charles Chanc22cef32016-04-29 14:38:22 -070073 });
74 }
Charles Chan1eaf4802016-04-18 13:44:03 -070075
Charles Chanc22cef32016-04-29 14:38:22 -070076 protected void processHostAddedEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070077 processHostAdded(event.subject());
Charles Chanc22cef32016-04-29 14:38:22 -070078 }
79
Charles Chan41f5ec02016-06-13 18:54:31 -070080 protected void processHostAdded(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -070081 MacAddress mac = host.mac();
82 VlanId vlanId = host.vlan();
83 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -070084 DeviceId deviceId = location.deviceId();
85 PortNumber port = location.port();
Charles Chan370a65b2016-05-10 17:29:47 -070086 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -080087 log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -070088
Charles Chan370a65b2016-05-10 17:29:47 -070089 if (accepted(host)) {
Charles Chan9595e6a2017-06-15 19:25:25 -070090 processBridgingRule(deviceId, port, mac, vlanId, false);
Charles Chan1eaf4802016-04-18 13:44:03 -070091 ips.forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -070092 processRoutingRule(deviceId, port, mac, vlanId, ip, false);
Charles Chan1eaf4802016-04-18 13:44:03 -070093 });
Charles Chanc22cef32016-04-29 14:38:22 -070094 }
Charles Chan1eaf4802016-04-18 13:44:03 -070095 }
96
Charles Chanc22cef32016-04-29 14:38:22 -070097 protected void processHostRemoveEvent(HostEvent event) {
Charles Chan41f5ec02016-06-13 18:54:31 -070098 processHostRemoved(event.subject());
99 }
100
101 protected void processHostRemoved(Host host) {
102 MacAddress mac = host.mac();
103 VlanId vlanId = host.vlan();
104 HostLocation location = host.location();
Charles Chanc22cef32016-04-29 14:38:22 -0700105 DeviceId deviceId = location.deviceId();
106 PortNumber port = location.port();
Charles Chan41f5ec02016-06-13 18:54:31 -0700107 Set<IpAddress> ips = host.ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800108 log.info("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
Charles Chanc22cef32016-04-29 14:38:22 -0700109
Charles Chan41f5ec02016-06-13 18:54:31 -0700110 if (accepted(host)) {
Charles Chan9595e6a2017-06-15 19:25:25 -0700111 processBridgingRule(deviceId, port, mac, vlanId, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700112 ips.forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -0700113 processRoutingRule(deviceId, port, mac, vlanId, ip, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700114 });
115 }
116 }
117
118 protected void processHostMovedEvent(HostEvent event) {
119 MacAddress mac = event.subject().mac();
120 VlanId vlanId = event.subject().vlan();
121 HostLocation prevLocation = event.prevSubject().location();
122 DeviceId prevDeviceId = prevLocation.deviceId();
123 PortNumber prevPort = prevLocation.port();
124 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
125 HostLocation newLocation = event.subject().location();
126 DeviceId newDeviceId = newLocation.deviceId();
127 PortNumber newPort = newLocation.port();
128 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800129 log.info("Host {}/{} is moved from {}:{} to {}:{}",
Charles Chanc22cef32016-04-29 14:38:22 -0700130 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
131
Charles Chan370a65b2016-05-10 17:29:47 -0700132 if (accepted(event.prevSubject())) {
Charles Chan9595e6a2017-06-15 19:25:25 -0700133 processBridgingRule(prevDeviceId, prevPort, mac, vlanId, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700134 prevIps.forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -0700135 processRoutingRule(prevDeviceId, prevPort, mac, vlanId, ip, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700136 });
137 }
138
Charles Chan370a65b2016-05-10 17:29:47 -0700139 if (accepted(event.subject())) {
Charles Chan9595e6a2017-06-15 19:25:25 -0700140 processBridgingRule(newDeviceId, newPort, mac, vlanId, false);
Charles Chanc22cef32016-04-29 14:38:22 -0700141 newIps.forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -0700142 processRoutingRule(newDeviceId, newPort, mac, vlanId, ip, false);
Charles Chanc22cef32016-04-29 14:38:22 -0700143 });
144 }
145 }
146
147 protected void processHostUpdatedEvent(HostEvent event) {
148 MacAddress mac = event.subject().mac();
149 VlanId vlanId = event.subject().vlan();
150 HostLocation prevLocation = event.prevSubject().location();
151 DeviceId prevDeviceId = prevLocation.deviceId();
152 PortNumber prevPort = prevLocation.port();
153 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
154 HostLocation newLocation = event.subject().location();
155 DeviceId newDeviceId = newLocation.deviceId();
156 PortNumber newPort = newLocation.port();
157 Set<IpAddress> newIps = event.subject().ipAddresses();
Saurav Dasf9332192017-02-18 14:05:44 -0800158 log.info("Host {}/{} is updated", mac, vlanId);
Charles Chanc22cef32016-04-29 14:38:22 -0700159
Charles Chan370a65b2016-05-10 17:29:47 -0700160 if (accepted(event.prevSubject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700161 // Revoke previous IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800162 Sets.difference(prevIps, newIps).forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -0700163 processRoutingRule(prevDeviceId, prevPort, mac, vlanId, ip, true);
Charles Chanc22cef32016-04-29 14:38:22 -0700164 });
165 }
166
Charles Chan370a65b2016-05-10 17:29:47 -0700167 if (accepted(event.subject())) {
Charles Chanc22cef32016-04-29 14:38:22 -0700168 // Populate new IP table entry
Saurav Dasf9332192017-02-18 14:05:44 -0800169 Sets.difference(newIps, prevIps).forEach(ip -> {
Charles Chan9595e6a2017-06-15 19:25:25 -0700170 processRoutingRule(newDeviceId, newPort, mac, vlanId, ip, false);
Charles Chanc22cef32016-04-29 14:38:22 -0700171 });
172 }
173 }
174
175 /**
Charles Chan18fa4252017-02-08 16:10:40 -0800176 * Generates a forwarding objective builder for bridging rules.
177 * <p>
178 * The forwarding objective bridges packets destined to a given MAC to
179 * given port on given device.
Charles Chanc22cef32016-04-29 14:38:22 -0700180 *
181 * @param deviceId Device that host attaches to
182 * @param mac MAC address of the host
Charles Chan90772a72017-02-08 15:52:08 -0800183 * @param hostVlanId VLAN ID of the host
Charles Chanc22cef32016-04-29 14:38:22 -0700184 * @param outport Port that host attaches to
Saurav Das2cb38292017-03-29 19:09:17 -0700185 * @param revoke true if forwarding objective is meant to revoke forwarding rule
Charles Chanc22cef32016-04-29 14:38:22 -0700186 * @return Forwarding objective builder
187 */
Charles Chan18fa4252017-02-08 16:10:40 -0800188 private ForwardingObjective.Builder bridgingFwdObjBuilder(
Charles Chan90772a72017-02-08 15:52:08 -0800189 DeviceId deviceId, MacAddress mac, VlanId hostVlanId,
Saurav Das2cb38292017-03-29 19:09:17 -0700190 PortNumber outport, boolean revoke) {
Charles Chan90772a72017-02-08 15:52:08 -0800191 ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
192 VlanId untaggedVlan = srManager.getUntaggedVlanId(connectPoint);
193 Set<VlanId> taggedVlans = srManager.getTaggedVlanId(connectPoint);
194 VlanId nativeVlan = srManager.getNativeVlanId(connectPoint);
Charles Chan1eaf4802016-04-18 13:44:03 -0700195
Charles Chan90772a72017-02-08 15:52:08 -0800196 // Create host selector
Charles Chan1eaf4802016-04-18 13:44:03 -0700197 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
198 sbuilder.matchEthDst(mac);
Charles Chan1eaf4802016-04-18 13:44:03 -0700199
Charles Chan90772a72017-02-08 15:52:08 -0800200 // Create host treatment
Charles Chan1eaf4802016-04-18 13:44:03 -0700201 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
Charles Chan1eaf4802016-04-18 13:44:03 -0700202 tbuilder.immediate().setOutput(outport);
203
Charles Chan90772a72017-02-08 15:52:08 -0800204 // Create host meta
205 TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
206
207 // Adjust the selector, treatment and meta according to VLAN configuration
208 if (taggedVlans.contains(hostVlanId)) {
209 sbuilder.matchVlanId(hostVlanId);
210 mbuilder.matchVlanId(hostVlanId);
211 } else if (hostVlanId.equals(VlanId.NONE)) {
212 if (untaggedVlan != null) {
213 sbuilder.matchVlanId(untaggedVlan);
214 mbuilder.matchVlanId(untaggedVlan);
215 tbuilder.immediate().popVlan();
216 } else if (nativeVlan != null) {
217 sbuilder.matchVlanId(nativeVlan);
218 mbuilder.matchVlanId(nativeVlan);
219 tbuilder.immediate().popVlan();
220 } else {
Charles Chan184e0242017-05-26 14:23:58 -0700221 log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
222 "vlan config", mac, hostVlanId, connectPoint);
223 return null;
Charles Chan90772a72017-02-08 15:52:08 -0800224 }
225 } else {
226 log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
227 mac, hostVlanId, connectPoint);
228 return null;
229 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700230
231 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
Saurav Das2cb38292017-03-29 19:09:17 -0700232 // If the objective is to revoke an existing rule, and for some reason
233 // the next-objective does not exist, then a new one should not be created
Charles Chan1eaf4802016-04-18 13:44:03 -0700234 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
Saurav Das2cb38292017-03-29 19:09:17 -0700235 tbuilder.build(), mbuilder.build(), !revoke);
Saurav Das07c74602016-04-27 18:35:50 -0700236 if (portNextObjId == -1) {
Charles Chan90772a72017-02-08 15:52:08 -0800237 // Warning log will come from getPortNextObjective method
Saurav Das07c74602016-04-27 18:35:50 -0700238 return null;
239 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700240
241 return DefaultForwardingObjective.builder()
242 .withFlag(ForwardingObjective.Flag.SPECIFIC)
243 .withSelector(sbuilder.build())
244 .nextStep(portNextObjId)
245 .withPriority(100)
246 .fromApp(srManager.appId)
247 .makePermanent();
248 }
249
Charles Chanc22cef32016-04-29 14:38:22 -0700250 /**
Charles Chan9595e6a2017-06-15 19:25:25 -0700251 * Populate or revoke a bridging rule on given deviceId that matches given mac, given vlan and
252 * output to given port.
253 *
254 * @param deviceId device ID
255 * @param port port
256 * @param mac mac address
257 * @param vlanId VLAN ID
258 * @param revoke true to revoke the rule; false to populate
259 */
260 private void processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
261 VlanId vlanId, boolean revoke) {
262 log.debug("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
263 mac, vlanId, deviceId, port);
264
265 ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, revoke);
266 if (fob == null) {
267 log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
268 return;
269 }
270
271 ObjectiveContext context = new DefaultObjectiveContext(
272 (objective) -> log.debug("Brigding rule for {}/{} {}", mac, vlanId,
273 revoke ? "revoked" : "populated"),
274 (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
275 revoke ? "revoked" : "populated", mac, vlanId, error));
276 flowObjectiveService.forward(deviceId, revoke ? fob.remove(context) : fob.add(context));
277 }
278
279 /**
280 * Populate or revoke a routing rule on given deviceId that matches given ip,
281 * set destination mac to given mac, set vlan to given vlan and output to given port.
282 *
283 * @param deviceId device ID
284 * @param port port
285 * @param mac mac address
286 * @param vlanId VLAN ID
287 * @param ip IP address
288 * @param revoke true to revoke the rule; false to populate
289 */
290 private void processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
291 VlanId vlanId, IpAddress ip, boolean revoke) {
292 ConnectPoint location = new ConnectPoint(deviceId, port);
293 if (srManager.deviceConfiguration.inSameSubnet(location, ip)) {
294 log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating",
295 ip, location);
296 if (revoke) {
297 srManager.routingRulePopulator.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
298 } else {
299 srManager.routingRulePopulator.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
300 }
301 }
302 }
303
304 /**
Charles Chandebfea32016-10-24 14:52:01 -0700305 * Determines whether a host should be accepted by SR or not.
Charles Chan370a65b2016-05-10 17:29:47 -0700306 *
307 * @param host host to be checked
308 * @return true if segment routing accepts the host
309 */
310 private boolean accepted(Host host) {
Charles Chan370a65b2016-05-10 17:29:47 -0700311 SegmentRoutingAppConfig appConfig = srManager.cfgService
312 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
Charles Chan3bf64b92016-05-20 10:55:40 -0700313
314 boolean accepted = appConfig == null ||
315 (!appConfig.suppressHostByProvider().contains(host.providerId().id()) &&
316 !appConfig.suppressHostByPort().contains(host.location()));
Charles Chan370a65b2016-05-10 17:29:47 -0700317 if (!accepted) {
318 log.info("Ignore suppressed host {}", host.id());
319 }
320 return accepted;
321 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700322}