blob: ffbdf107d87a1ec97d85dad3590b1a21e493d508 [file] [log] [blame]
Pingping Line28ae4c2015-03-13 11:37:03 -07001/*
2 * Copyright 2015 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 */
16package org.onosproject.reactive.routing;
Aaron Kruglikov07a923d2015-07-03 13:30:57 -070017
Pingping Line28ae4c2015-03-13 11:37:03 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Pingping Linc9e16bf2015-04-10 14:42:41 -070023import org.onlab.packet.ARP;
Jonathan Hart3217d1b2015-06-30 16:08:43 -070024import org.onlab.packet.EthType;
Pingping Line28ae4c2015-03-13 11:37:03 -070025import org.onlab.packet.Ethernet;
26import org.onlab.packet.IPv4;
Pingping Linc9e16bf2015-04-10 14:42:41 -070027import org.onlab.packet.Ip4Address;
Jonathan Hart9a426f82015-09-03 15:43:13 +020028import org.onlab.packet.Ip6Address;
Pingping Line28ae4c2015-03-13 11:37:03 -070029import org.onlab.packet.IpAddress;
Jonathan Hart9a426f82015-09-03 15:43:13 +020030import org.onlab.packet.IpPrefix;
Pingping Line28ae4c2015-03-13 11:37:03 -070031import org.onlab.packet.MacAddress;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020034import org.onosproject.incubator.net.intf.Interface;
35import org.onosproject.incubator.net.intf.InterfaceService;
Pingping Line28ae4c2015-03-13 11:37:03 -070036import org.onosproject.net.ConnectPoint;
Jonathan Hart9a426f82015-09-03 15:43:13 +020037import org.onosproject.net.Host;
Pingping Line28ae4c2015-03-13 11:37:03 -070038import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.TrafficSelector;
41import org.onosproject.net.flow.TrafficTreatment;
Jonathan Hart9a426f82015-09-03 15:43:13 +020042import org.onosproject.net.host.HostService;
Pingping Line28ae4c2015-03-13 11:37:03 -070043import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.InboundPacket;
45import org.onosproject.net.packet.OutboundPacket;
46import org.onosproject.net.packet.PacketContext;
Pingping Line28ae4c2015-03-13 11:37:03 -070047import org.onosproject.net.packet.PacketProcessor;
48import org.onosproject.net.packet.PacketService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020049import org.onosproject.routing.IntentRequestListener;
Jonathan Hart365335e2015-12-10 11:09:53 -080050import org.onosproject.routing.IntentSynchronizationService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020051import org.onosproject.routing.RouteEntry;
Pingping Line28ae4c2015-03-13 11:37:03 -070052import org.onosproject.routing.RoutingService;
Pingping Linc9e16bf2015-04-10 14:42:41 -070053import org.onosproject.routing.config.RoutingConfigurationService;
Pingping Line28ae4c2015-03-13 11:37:03 -070054import org.slf4j.Logger;
55
Jonathan Hart3217d1b2015-06-30 16:08:43 -070056import java.nio.ByteBuffer;
Jonathan Hart9a426f82015-09-03 15:43:13 +020057import java.util.Optional;
58import java.util.Set;
Jonathan Hart3217d1b2015-06-30 16:08:43 -070059
Jonathan Hart9a426f82015-09-03 15:43:13 +020060import static com.google.common.base.Preconditions.checkNotNull;
Aaron Kruglikov07a923d2015-07-03 13:30:57 -070061import static org.onlab.packet.Ethernet.TYPE_ARP;
62import static org.onlab.packet.Ethernet.TYPE_IPV4;
63import static org.onosproject.net.packet.PacketPriority.REACTIVE;
Jonathan Hart3217d1b2015-06-30 16:08:43 -070064import static org.slf4j.LoggerFactory.getLogger;
65
Pingping Line28ae4c2015-03-13 11:37:03 -070066/**
67 * This is reactive routing to handle 3 cases:
68 * (1) one host wants to talk to another host, both two hosts are in
69 * SDN network.
70 * (2) one host in SDN network wants to talk to another host in Internet.
71 * (3) one host from Internet wants to talk to another host in SDN network.
72 */
73@Component(immediate = true)
74public class SdnIpReactiveRouting {
75
76 private static final String APP_NAME = "org.onosproject.reactive.routing";
77 private final Logger log = getLogger(getClass());
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected PacketService packetService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected RoutingService routingService;
87
Pingping Linc9e16bf2015-04-10 14:42:41 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart365335e2015-12-10 11:09:53 -080089 protected IntentSynchronizationService intentSynchronizer;
Jonathan Hart9a426f82015-09-03 15:43:13 +020090
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pingping Linc9e16bf2015-04-10 14:42:41 -070092 protected RoutingConfigurationService config;
93
Jonathan Hart9a426f82015-09-03 15:43:13 +020094 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected InterfaceService interfaceService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected HostService hostService;
99
Pingping Line28ae4c2015-03-13 11:37:03 -0700100 private ApplicationId appId;
101
Jonathan Hart9a426f82015-09-03 15:43:13 +0200102 private IntentRequestListener intentRequestListener;
103
Pingping Line28ae4c2015-03-13 11:37:03 -0700104 private ReactiveRoutingProcessor processor =
105 new ReactiveRoutingProcessor();
106
107 @Activate
108 public void activate() {
109 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200110
111 intentRequestListener = new ReactiveRoutingFib(appId, hostService,
Jonathan Hart365335e2015-12-10 11:09:53 -0800112 config, interfaceService, intentSynchronizer);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200113
Brian O'Connor3b783262015-07-29 17:49:24 -0700114 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700115 requestIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700116 log.info("SDN-IP Reactive Routing Started");
117 }
118
119 @Deactivate
120 public void deactivate() {
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700121 withdrawIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700122 packetService.removeProcessor(processor);
123 processor = null;
124 log.info("SDN-IP Reactive Routing Stopped");
125 }
126
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700127 /**
128 * Request packet in via the PacketService.
129 */
130 private void requestIntercepts() {
131 //TODO: to support IPv6 later
132 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
133 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700134 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700135 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700136 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700137 }
138
139 /**
140 * Cancel request for packet in via PacketService.
141 */
142 private void withdrawIntercepts() {
143 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
144 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700145 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700146 selector = DefaultTrafficSelector.builder();
147 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700148 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700149 }
150
Pingping Line28ae4c2015-03-13 11:37:03 -0700151 private class ReactiveRoutingProcessor implements PacketProcessor {
152 @Override
153 public void process(PacketContext context) {
154
155 InboundPacket pkt = context.inPacket();
156 Ethernet ethPkt = pkt.parsed();
157 if (ethPkt == null) {
158 return;
159 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700160 ConnectPoint srcConnectPoint = pkt.receivedFrom();
Pingping Line28ae4c2015-03-13 11:37:03 -0700161
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700162 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
163 case ARP:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700164 ARP arpPacket = (ARP) ethPkt.getPayload();
165 Ip4Address targetIpAddress = Ip4Address
166 .valueOf(arpPacket.getTargetProtocolAddress());
167 // Only when it is an ARP request packet and the target IP
168 // address is a virtual gateway IP address, then it will be
169 // processed.
170 if (arpPacket.getOpCode() == ARP.OP_REQUEST
171 && config.isVirtualGatewayIpAddress(targetIpAddress)) {
172 MacAddress gatewayMacAddress =
173 config.getVirtualGatewayMacAddress();
174 if (gatewayMacAddress == null) {
175 break;
176 }
177 Ethernet eth = ARP.buildArpReply(targetIpAddress,
178 gatewayMacAddress,
179 ethPkt);
180
181 TrafficTreatment.Builder builder =
182 DefaultTrafficTreatment.builder();
183 builder.setOutput(srcConnectPoint.port());
184 packetService.emit(new DefaultOutboundPacket(
185 srcConnectPoint.deviceId(),
186 builder.build(),
187 ByteBuffer.wrap(eth.serialize())));
188 }
189 break;
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700190 case IPV4:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700191 // Parse packet
192 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
193 IpAddress dstIp =
194 IpAddress.valueOf(ipv4Packet.getDestinationAddress());
195 IpAddress srcIp =
196 IpAddress.valueOf(ipv4Packet.getSourceAddress());
197 MacAddress srcMac = ethPkt.getSourceMAC();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200198 packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700199
200 // TODO emit packet first or packetReactiveProcessor first
201 ConnectPoint egressConnectPoint = null;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200202 egressConnectPoint = getEgressConnectPoint(dstIp);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700203 if (egressConnectPoint != null) {
204 forwardPacketToDst(context, egressConnectPoint);
205 }
206 break;
207 default:
208 break;
Pingping Line28ae4c2015-03-13 11:37:03 -0700209 }
210 }
211 }
212
213 /**
Jonathan Hart9a426f82015-09-03 15:43:13 +0200214 * Routes packet reactively.
215 *
216 * @param dstIpAddress the destination IP address of a packet
217 * @param srcIpAddress the source IP address of a packet
218 * @param srcConnectPoint the connect point where a packet comes from
219 * @param srcMacAddress the source MAC address of a packet
220 */
221 private void packetReactiveProcessor(IpAddress dstIpAddress,
222 IpAddress srcIpAddress,
223 ConnectPoint srcConnectPoint,
224 MacAddress srcMacAddress) {
225 checkNotNull(dstIpAddress);
226 checkNotNull(srcIpAddress);
227 checkNotNull(srcConnectPoint);
228 checkNotNull(srcMacAddress);
229
230 //
231 // Step1: Try to update the existing intent first if it exists.
232 //
233 IpPrefix ipPrefix = null;
234 RouteEntry routeEntry = null;
235 if (config.isIpAddressLocal(dstIpAddress)) {
236 if (dstIpAddress.isIp4()) {
237 ipPrefix = IpPrefix.valueOf(dstIpAddress,
238 Ip4Address.BIT_LENGTH);
239 } else {
240 ipPrefix = IpPrefix.valueOf(dstIpAddress,
241 Ip6Address.BIT_LENGTH);
242 }
243 } else {
244 // Get IP prefix from BGP route table
245 routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
246 if (routeEntry != null) {
247 ipPrefix = routeEntry.prefix();
248 }
249 }
250 if (ipPrefix != null
251 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
252 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
253 srcConnectPoint);
254 return;
255 }
256
257 //
258 // Step2: There is no existing intent for the destination IP address.
259 // Check whether it is necessary to create a new one. If necessary then
260 // create a new one.
261 //
262 TrafficType trafficType =
263 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
264
265 switch (trafficType) {
266 case HOST_TO_INTERNET:
267 // If the destination IP address is outside the local SDN network.
268 // The Step 1 has already handled it. We do not need to do anything here.
269 intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
270 ipPrefix, routeEntry.nextHop());
271 break;
272 case INTERNET_TO_HOST:
273 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
274 break;
275 case HOST_TO_HOST:
276 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
277 srcIpAddress, srcMacAddress, srcConnectPoint);
278 break;
279 case INTERNET_TO_INTERNET:
280 log.trace("This is transit traffic, "
281 + "the intent should be preinstalled already");
282 break;
283 case DROP:
284 // TODO here should setUpDropPacketIntent(...);
285 // We need a new type of intent here.
286 break;
287 case UNKNOWN:
288 log.trace("This is unknown traffic, so we do nothing");
289 break;
290 default:
291 break;
292 }
293 }
294
295 /**
296 * Classifies the traffic and return the traffic type.
297 *
298 * @param srcConnectPoint the connect point where the packet comes from
299 * @param dstIp the destination IP address in packet
300 * @return the traffic type which this packet belongs to
301 */
302 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
303 IpAddress dstIp) {
304 LocationType dstIpLocationType = getLocationType(dstIp);
305 Optional<Interface> srcInterface =
306 interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
Pingping Lin9b85c032015-10-05 18:16:27 -0700307 Set<ConnectPoint> ingressPoints = config.getBgpPeerConnectPoints();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200308
309 switch (dstIpLocationType) {
310 case INTERNET:
Pingping Lin9b85c032015-10-05 18:16:27 -0700311 if (srcInterface.isPresent() &&
312 (!ingressPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200313 return TrafficType.HOST_TO_INTERNET;
314 } else {
315 return TrafficType.INTERNET_TO_INTERNET;
316 }
317 case LOCAL:
Pingping Lin9b85c032015-10-05 18:16:27 -0700318 if (srcInterface.isPresent() &&
319 (!ingressPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200320 return TrafficType.HOST_TO_HOST;
321 } else {
322 // TODO Currently we only consider local public prefixes.
323 // In the future, we will consider the local private prefixes.
324 // If dstIpLocationType is a local private, we should return
325 // TrafficType.DROP.
326 return TrafficType.INTERNET_TO_HOST;
327 }
328 case NO_ROUTE:
329 return TrafficType.DROP;
330 default:
331 return TrafficType.UNKNOWN;
332 }
333 }
334
335 /**
336 * Evaluates the location of an IP address and returns the location type.
337 *
338 * @param ipAddress the IP address to evaluate
339 * @return the IP address location type
340 */
341 private LocationType getLocationType(IpAddress ipAddress) {
342 if (config.isIpAddressLocal(ipAddress)) {
343 return LocationType.LOCAL;
344 } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) {
345 return LocationType.INTERNET;
346 } else {
347 return LocationType.NO_ROUTE;
348 }
349 }
350
351 public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
352 LocationType type = getLocationType(dstIpAddress);
353 if (type == LocationType.LOCAL) {
354 Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
355 if (!hosts.isEmpty()) {
356 return hosts.iterator().next().location();
357 } else {
358 hostService.startMonitoringIp(dstIpAddress);
359 return null;
360 }
361 } else if (type == LocationType.INTERNET) {
362 IpAddress nextHopIpAddress = null;
363 RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
364 if (routeEntry != null) {
365 nextHopIpAddress = routeEntry.nextHop();
366 Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
367 if (it != null) {
368 return it.connectPoint();
369 } else {
370 return null;
371 }
372 } else {
373 return null;
374 }
375 } else {
376 return null;
377 }
378 }
379
380 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700381 * Emits the specified packet onto the network.
382 *
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700383 * @param context the packet context
Pingping Line28ae4c2015-03-13 11:37:03 -0700384 * @param connectPoint the connect point where the packet should be
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700385 * sent out
Pingping Line28ae4c2015-03-13 11:37:03 -0700386 */
387 private void forwardPacketToDst(PacketContext context,
388 ConnectPoint connectPoint) {
389 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
390 .setOutput(connectPoint.port()).build();
391 OutboundPacket packet =
392 new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
393 context.inPacket().unparsed());
394 packetService.emit(packet);
395 log.trace("sending packet: {}", packet);
396 }
397
398}
399