blob: 05bba2d89466db105c9a03857dbe684d5130049c [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 Chanc22cef32016-04-29 14:38:22 -070027import org.onosproject.net.HostLocation;
Charles Chan1eaf4802016-04-18 13:44:03 -070028import org.onosproject.net.PortNumber;
29import org.onosproject.net.flow.DefaultTrafficSelector;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficSelector;
32import org.onosproject.net.flow.TrafficTreatment;
33import org.onosproject.net.flowobjective.DefaultForwardingObjective;
34import org.onosproject.net.flowobjective.DefaultObjectiveContext;
35import org.onosproject.net.flowobjective.FlowObjectiveService;
36import org.onosproject.net.flowobjective.ForwardingObjective;
37import org.onosproject.net.flowobjective.ObjectiveContext;
38import org.onosproject.net.host.HostEvent;
39import org.onosproject.net.host.HostService;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.util.Set;
44
45/**
46 * Handles host-related events.
47 */
48public class HostHandler {
49 private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
50 private 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
Saurav Das07c74602016-04-27 18:35:50 -070065 protected void readInitialHosts(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 Chanc22cef32016-04-29 14:38:22 -070072 processHostAddedEventInternal(host.mac(), host.vlan(),
73 host.location(), host.ipAddresses());
74 });
75 }
Charles Chan1eaf4802016-04-18 13:44:03 -070076
Charles Chanc22cef32016-04-29 14:38:22 -070077 protected void processHostAddedEvent(HostEvent event) {
78 processHostAddedEventInternal(event.subject().mac(), event.subject().vlan(),
79 event.subject().location(), event.subject().ipAddresses());
80 }
81
82 private void processHostAddedEventInternal(MacAddress mac, VlanId vlanId,
83 HostLocation location, Set<IpAddress> ips) {
84 DeviceId deviceId = location.deviceId();
85 PortNumber port = location.port();
86 log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
87
88 if (!srManager.deviceConfiguration.suppressHost().contains(location)) {
Charles Chan1eaf4802016-04-18 13:44:03 -070089 // Populate bridging table entry
Charles Chanc22cef32016-04-29 14:38:22 -070090 log.debug("Populate L2 table entry for host {} at {}:{}",
91 mac, deviceId, port);
Charles Chan1eaf4802016-04-18 13:44:03 -070092 ForwardingObjective.Builder fob =
Charles Chanc22cef32016-04-29 14:38:22 -070093 hostFwdObjBuilder(deviceId, mac, vlanId, port);
Saurav Das07c74602016-04-27 18:35:50 -070094 if (fob == null) {
Charles Chanc22cef32016-04-29 14:38:22 -070095 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
Saurav Das07c74602016-04-27 18:35:50 -070096 return;
97 }
Charles Chan1eaf4802016-04-18 13:44:03 -070098 ObjectiveContext context = new DefaultObjectiveContext(
Charles Chanc22cef32016-04-29 14:38:22 -070099 (objective) -> log.debug("Host rule for {}/{} populated", mac, vlanId),
Charles Chan1eaf4802016-04-18 13:44:03 -0700100 (objective, error) ->
Charles Chanc22cef32016-04-29 14:38:22 -0700101 log.warn("Failed to populate host rule for {}/{}: {}", mac, vlanId, error));
Charles Chan1eaf4802016-04-18 13:44:03 -0700102 flowObjectiveService.forward(deviceId, fob.add(context));
103
Charles Chan1eaf4802016-04-18 13:44:03 -0700104 ips.forEach(ip -> {
Charles Chanc22cef32016-04-29 14:38:22 -0700105 // Populate IP table entry
Charles Chan1eaf4802016-04-18 13:44:03 -0700106 if (ip.isIp4()) {
Charles Chanc22cef32016-04-29 14:38:22 -0700107 addPerHostRoute(location, ip.getIp4Address());
Charles Chan1eaf4802016-04-18 13:44:03 -0700108 srManager.routingRulePopulator.populateIpRuleForHost(
109 deviceId, ip.getIp4Address(), mac, port);
110 }
111 });
Charles Chanc22cef32016-04-29 14:38:22 -0700112 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700113 }
114
Charles Chanc22cef32016-04-29 14:38:22 -0700115 protected void processHostRemoveEvent(HostEvent event) {
116 MacAddress mac = event.subject().mac();
117 VlanId vlanId = event.subject().vlan();
118 HostLocation location = event.subject().location();
119 DeviceId deviceId = location.deviceId();
120 PortNumber port = location.port();
121 Set<IpAddress> ips = event.subject().ipAddresses();
122 log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
123
124 if (!srManager.deviceConfiguration.suppressHost()
125 .contains(new ConnectPoint(deviceId, port))) {
126 // Revoke bridging table entry
127 ForwardingObjective.Builder fob =
128 hostFwdObjBuilder(deviceId, mac, vlanId, port);
129 if (fob == null) {
130 log.warn("Fail to create fwd obj for host {}/{}. Abort.", mac, vlanId);
131 return;
132 }
133 ObjectiveContext context = new DefaultObjectiveContext(
134 (objective) -> log.debug("Host rule for {} revoked", event.subject()),
135 (objective, error) ->
136 log.warn("Failed to revoke host rule for {}: {}", event.subject(), error));
137 flowObjectiveService.forward(deviceId, fob.remove(context));
138
139 // Revoke IP table entry
140 ips.forEach(ip -> {
141 if (ip.isIp4()) {
142 removePerHostRoute(location, ip.getIp4Address());
143 srManager.routingRulePopulator.revokeIpRuleForHost(
144 deviceId, ip.getIp4Address(), mac, port);
145 }
146 });
147 }
148 }
149
150 protected void processHostMovedEvent(HostEvent event) {
151 MacAddress mac = event.subject().mac();
152 VlanId vlanId = event.subject().vlan();
153 HostLocation prevLocation = event.prevSubject().location();
154 DeviceId prevDeviceId = prevLocation.deviceId();
155 PortNumber prevPort = prevLocation.port();
156 Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
157 HostLocation newLocation = event.subject().location();
158 DeviceId newDeviceId = newLocation.deviceId();
159 PortNumber newPort = newLocation.port();
160 Set<IpAddress> newIps = event.subject().ipAddresses();
161 log.debug("Host {}/{} is moved from {}:{} to {}:{}",
162 mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
163
164 if (!srManager.deviceConfiguration.suppressHost()
165 .contains(new ConnectPoint(prevDeviceId, prevPort))) {
166 // 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 -> {
181 if (ip.isIp4()) {
182 removePerHostRoute(prevLocation, ip.getIp4Address());
183 srManager.routingRulePopulator.revokeIpRuleForHost(
184 prevDeviceId, ip.getIp4Address(), mac, prevPort);
185 }
186 });
187 }
188
189 if (!srManager.deviceConfiguration.suppressHost()
190 .contains(new ConnectPoint(newDeviceId, newPort))) {
191 // 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
228 if (!srManager.deviceConfiguration.suppressHost()
229 .contains(new ConnectPoint(prevDeviceId, prevPort))) {
230 // 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
240 if (!srManager.deviceConfiguration.suppressHost()
241 .contains(new ConnectPoint(newDeviceId, newPort))) {
242 // Populate new IP table entry
243 newIps.forEach(ip -> {
244 if (ip.isIp4()) {
245 addPerHostRoute(newLocation, ip.getIp4Address());
246 srManager.routingRulePopulator.populateIpRuleForHost(
247 newDeviceId, ip.getIp4Address(), mac, newPort);
248 }
249 });
250 }
251 }
252
253 /**
254 * Generates the forwarding objective builder for the host rules.
255 *
256 * @param deviceId Device that host attaches to
257 * @param mac MAC address of the host
258 * @param vlanId VLAN ID of the host
259 * @param outport Port that host attaches to
260 * @return Forwarding objective builder
261 */
262 private ForwardingObjective.Builder hostFwdObjBuilder(
Charles Chan1eaf4802016-04-18 13:44:03 -0700263 DeviceId deviceId, MacAddress mac, VlanId vlanId,
264 PortNumber outport) {
Charles Chanc22cef32016-04-29 14:38:22 -0700265 // Get assigned VLAN for the subnets
Charles Chan1eaf4802016-04-18 13:44:03 -0700266 VlanId outvlan = null;
267 Ip4Prefix subnet = srManager.deviceConfiguration.getPortSubnet(deviceId, outport);
268 if (subnet == null) {
269 outvlan = VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET);
270 } else {
271 outvlan = srManager.getSubnetAssignedVlanId(deviceId, subnet);
272 }
273
274 // match rule
275 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
276 sbuilder.matchEthDst(mac);
Charles Chanc22cef32016-04-29 14:38:22 -0700277 /*
278 * Note: for untagged packets, match on the assigned VLAN.
279 * for tagged packets, match on its incoming VLAN.
280 */
Charles Chan1eaf4802016-04-18 13:44:03 -0700281 if (vlanId.equals(VlanId.NONE)) {
282 sbuilder.matchVlanId(outvlan);
283 } else {
284 sbuilder.matchVlanId(vlanId);
285 }
286
287 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
288 tbuilder.immediate().popVlan();
289 tbuilder.immediate().setOutput(outport);
290
291 // for switch pipelines that need it, provide outgoing vlan as metadata
292 TrafficSelector meta = DefaultTrafficSelector.builder()
293 .matchVlanId(outvlan).build();
294
295 // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
296 int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
297 tbuilder.build(),
298 meta);
Saurav Das07c74602016-04-27 18:35:50 -0700299 if (portNextObjId == -1) {
300 // warning log will come from getPortNextObjective method
301 return null;
302 }
Charles Chan1eaf4802016-04-18 13:44:03 -0700303
304 return DefaultForwardingObjective.builder()
305 .withFlag(ForwardingObjective.Flag.SPECIFIC)
306 .withSelector(sbuilder.build())
307 .nextStep(portNextObjId)
308 .withPriority(100)
309 .fromApp(srManager.appId)
310 .makePermanent();
311 }
312
Charles Chanc22cef32016-04-29 14:38:22 -0700313 /**
314 * Add per host route to subnet list and populate the flow rule if the host
315 * does not belong to the configured subnet.
316 *
317 * @param location location of the host being added
318 * @param ip IP address of the host being added
319 */
320 private void addPerHostRoute(ConnectPoint location, Ip4Address ip) {
321 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
322 location.deviceId(), location.port());
323 if (portSubnet != null && !portSubnet.contains(ip)) {
324 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
325 srManager.deviceConfiguration.addSubnet(location, ip4Prefix);
326 srManager.defaultRoutingHandler.populateSubnet(location,
327 ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700328 }
329 }
330
Charles Chanc22cef32016-04-29 14:38:22 -0700331 /**
332 * Remove per host route from subnet list and revoke the flow rule if the
333 * host does not belong to the configured subnet.
334 *
335 * @param location location of the host being removed
336 * @param ip IP address of the host being removed
337 */
338 private void removePerHostRoute(ConnectPoint location, Ip4Address ip) {
339 Ip4Prefix portSubnet = srManager.deviceConfiguration.getPortSubnet(
340 location.deviceId(), location.port());
341 if (portSubnet != null && !portSubnet.contains(ip)) {
342 Ip4Prefix ip4Prefix = ip.toIpPrefix().getIp4Prefix();
343 srManager.deviceConfiguration.removeSubnet(location, ip4Prefix);
344 srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(ip4Prefix));
Charles Chan1eaf4802016-04-18 13:44:03 -0700345 }
346 }
347}