blob: 5b6ad9c7a189e67ce4d706d386f74c0988f8abe2 [file] [log] [blame]
Pingping Line28ae4c2015-03-13 11:37:03 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Pingping Line28ae4c2015-03-13 11:37: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 */
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;
Ray Milkeyfacf2862017-08-03 11:58:29 -070034import org.onosproject.net.intf.Interface;
35import org.onosproject.net.intf.InterfaceService;
Ray Milkey69ec8712017-08-08 13:00:43 -070036import org.onosproject.routeservice.Route;
37import org.onosproject.routeservice.RouteService;
Pingping Line28ae4c2015-03-13 11:37:03 -070038import org.onosproject.net.ConnectPoint;
Jonathan Hart9a426f82015-09-03 15:43:13 +020039import org.onosproject.net.Host;
Pingping Line28ae4c2015-03-13 11:37:03 -070040import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
Jonathan Hart9a426f82015-09-03 15:43:13 +020044import org.onosproject.net.host.HostService;
Pingping Line28ae4c2015-03-13 11:37:03 -070045import org.onosproject.net.packet.DefaultOutboundPacket;
46import org.onosproject.net.packet.InboundPacket;
47import org.onosproject.net.packet.OutboundPacket;
48import org.onosproject.net.packet.PacketContext;
Pingping Line28ae4c2015-03-13 11:37:03 -070049import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
Jonathan Hart470ed4f2017-01-31 16:52:28 -080051import org.onosproject.intentsync.IntentSynchronizationService;
Pingping Line28ae4c2015-03-13 11:37:03 -070052import org.slf4j.Logger;
53
Jonathan Hart92ca5d32016-04-14 18:05:52 -070054import java.nio.ByteBuffer;
55import java.util.Optional;
56import java.util.Set;
57
58import static com.google.common.base.Preconditions.checkNotNull;
59import static org.onlab.packet.Ethernet.TYPE_ARP;
60import static org.onlab.packet.Ethernet.TYPE_IPV4;
61import static org.onosproject.net.packet.PacketPriority.REACTIVE;
62import static org.slf4j.LoggerFactory.getLogger;
63
Pingping Line28ae4c2015-03-13 11:37:03 -070064/**
65 * This is reactive routing to handle 3 cases:
66 * (1) one host wants to talk to another host, both two hosts are in
67 * SDN network.
68 * (2) one host in SDN network wants to talk to another host in Internet.
69 * (3) one host from Internet wants to talk to another host in SDN network.
70 */
71@Component(immediate = true)
72public class SdnIpReactiveRouting {
73
74 private static final String APP_NAME = "org.onosproject.reactive.routing";
75 private final Logger log = getLogger(getClass());
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected CoreService coreService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected PacketService packetService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart92ca5d32016-04-14 18:05:52 -070084 protected RouteService routeService;
Pingping Line28ae4c2015-03-13 11:37:03 -070085
Pingping Linc9e16bf2015-04-10 14:42:41 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart365335e2015-12-10 11:09:53 -080087 protected IntentSynchronizationService intentSynchronizer;
Jonathan Hart9a426f82015-09-03 15:43:13 +020088
89 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hartbb782be2017-02-09 17:45:49 -080090 protected ReactiveRoutingConfigurationService config;
Pingping Linc9e16bf2015-04-10 14:42:41 -070091
Jonathan Hart9a426f82015-09-03 15:43:13 +020092 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 protected InterfaceService interfaceService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected HostService hostService;
97
Pingping Line28ae4c2015-03-13 11:37:03 -070098 private ApplicationId appId;
99
Jonathan Hart9a426f82015-09-03 15:43:13 +0200100 private IntentRequestListener intentRequestListener;
101
Pingping Line28ae4c2015-03-13 11:37:03 -0700102 private ReactiveRoutingProcessor processor =
103 new ReactiveRoutingProcessor();
104
105 @Activate
106 public void activate() {
107 appId = coreService.registerApplication(APP_NAME);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200108 intentRequestListener = new ReactiveRoutingFib(appId, hostService,
Pingping Lin9a445c82016-04-07 11:40:29 -0700109 interfaceService, intentSynchronizer);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200110
Brian O'Connor3b783262015-07-29 17:49:24 -0700111 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700112 requestIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700113 log.info("SDN-IP Reactive Routing Started");
114 }
115
116 @Deactivate
117 public void deactivate() {
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700118 withdrawIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700119 packetService.removeProcessor(processor);
120 processor = null;
121 log.info("SDN-IP Reactive Routing Stopped");
122 }
123
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700124 /**
125 * Request packet in via the PacketService.
126 */
127 private void requestIntercepts() {
128 //TODO: to support IPv6 later
129 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
130 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700131 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700132 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700133 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700134 }
135
136 /**
137 * Cancel request for packet in via PacketService.
138 */
139 private void withdrawIntercepts() {
140 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
141 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700142 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700143 selector = DefaultTrafficSelector.builder();
144 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700145 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700146 }
147
Pingping Line28ae4c2015-03-13 11:37:03 -0700148 private class ReactiveRoutingProcessor implements PacketProcessor {
149 @Override
150 public void process(PacketContext context) {
151
152 InboundPacket pkt = context.inPacket();
153 Ethernet ethPkt = pkt.parsed();
154 if (ethPkt == null) {
155 return;
156 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700157 ConnectPoint srcConnectPoint = pkt.receivedFrom();
Pingping Line28ae4c2015-03-13 11:37:03 -0700158
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700159 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
160 case ARP:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700161 ARP arpPacket = (ARP) ethPkt.getPayload();
162 Ip4Address targetIpAddress = Ip4Address
163 .valueOf(arpPacket.getTargetProtocolAddress());
164 // Only when it is an ARP request packet and the target IP
165 // address is a virtual gateway IP address, then it will be
166 // processed.
167 if (arpPacket.getOpCode() == ARP.OP_REQUEST
168 && config.isVirtualGatewayIpAddress(targetIpAddress)) {
169 MacAddress gatewayMacAddress =
170 config.getVirtualGatewayMacAddress();
171 if (gatewayMacAddress == null) {
172 break;
173 }
174 Ethernet eth = ARP.buildArpReply(targetIpAddress,
175 gatewayMacAddress,
176 ethPkt);
177
178 TrafficTreatment.Builder builder =
179 DefaultTrafficTreatment.builder();
180 builder.setOutput(srcConnectPoint.port());
181 packetService.emit(new DefaultOutboundPacket(
182 srcConnectPoint.deviceId(),
183 builder.build(),
184 ByteBuffer.wrap(eth.serialize())));
185 }
186 break;
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700187 case IPV4:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700188 // Parse packet
189 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
190 IpAddress dstIp =
191 IpAddress.valueOf(ipv4Packet.getDestinationAddress());
192 IpAddress srcIp =
193 IpAddress.valueOf(ipv4Packet.getSourceAddress());
194 MacAddress srcMac = ethPkt.getSourceMAC();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200195 packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700196
197 // TODO emit packet first or packetReactiveProcessor first
198 ConnectPoint egressConnectPoint = null;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200199 egressConnectPoint = getEgressConnectPoint(dstIp);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700200 if (egressConnectPoint != null) {
201 forwardPacketToDst(context, egressConnectPoint);
202 }
203 break;
204 default:
205 break;
Pingping Line28ae4c2015-03-13 11:37:03 -0700206 }
207 }
208 }
209
210 /**
Jonathan Hart9a426f82015-09-03 15:43:13 +0200211 * Routes packet reactively.
212 *
213 * @param dstIpAddress the destination IP address of a packet
214 * @param srcIpAddress the source IP address of a packet
215 * @param srcConnectPoint the connect point where a packet comes from
216 * @param srcMacAddress the source MAC address of a packet
217 */
218 private void packetReactiveProcessor(IpAddress dstIpAddress,
219 IpAddress srcIpAddress,
220 ConnectPoint srcConnectPoint,
221 MacAddress srcMacAddress) {
222 checkNotNull(dstIpAddress);
223 checkNotNull(srcIpAddress);
224 checkNotNull(srcConnectPoint);
225 checkNotNull(srcMacAddress);
226
227 //
228 // Step1: Try to update the existing intent first if it exists.
229 //
230 IpPrefix ipPrefix = null;
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700231 Route route = null;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200232 if (config.isIpAddressLocal(dstIpAddress)) {
233 if (dstIpAddress.isIp4()) {
234 ipPrefix = IpPrefix.valueOf(dstIpAddress,
235 Ip4Address.BIT_LENGTH);
236 } else {
237 ipPrefix = IpPrefix.valueOf(dstIpAddress,
238 Ip6Address.BIT_LENGTH);
239 }
240 } else {
241 // Get IP prefix from BGP route table
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700242 route = routeService.longestPrefixMatch(dstIpAddress);
243 if (route != null) {
244 ipPrefix = route.prefix();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200245 }
246 }
247 if (ipPrefix != null
248 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
249 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
250 srcConnectPoint);
251 return;
252 }
253
254 //
255 // Step2: There is no existing intent for the destination IP address.
256 // Check whether it is necessary to create a new one. If necessary then
257 // create a new one.
258 //
259 TrafficType trafficType =
260 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
261
262 switch (trafficType) {
263 case HOST_TO_INTERNET:
264 // If the destination IP address is outside the local SDN network.
265 // The Step 1 has already handled it. We do not need to do anything here.
266 intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700267 ipPrefix, route.nextHop());
Jonathan Hart9a426f82015-09-03 15:43:13 +0200268 break;
269 case INTERNET_TO_HOST:
270 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
271 break;
272 case HOST_TO_HOST:
273 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
274 srcIpAddress, srcMacAddress, srcConnectPoint);
275 break;
276 case INTERNET_TO_INTERNET:
277 log.trace("This is transit traffic, "
278 + "the intent should be preinstalled already");
279 break;
280 case DROP:
281 // TODO here should setUpDropPacketIntent(...);
282 // We need a new type of intent here.
283 break;
284 case UNKNOWN:
285 log.trace("This is unknown traffic, so we do nothing");
286 break;
287 default:
288 break;
289 }
290 }
291
292 /**
293 * Classifies the traffic and return the traffic type.
294 *
295 * @param srcConnectPoint the connect point where the packet comes from
296 * @param dstIp the destination IP address in packet
297 * @return the traffic type which this packet belongs to
298 */
299 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
Pingping Lin9a445c82016-04-07 11:40:29 -0700300 IpAddress dstIp) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200301 LocationType dstIpLocationType = getLocationType(dstIp);
302 Optional<Interface> srcInterface =
303 interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
Pingping Lin9a445c82016-04-07 11:40:29 -0700304
305 Set<ConnectPoint> bgpPeerConnectPoints = config.getBgpPeerConnectPoints();
306
307
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() &&
Pingping Lin9a445c82016-04-07 11:40:29 -0700312 (!bgpPeerConnectPoints.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() &&
Pingping Lin9a445c82016-04-07 11:40:29 -0700319 (!bgpPeerConnectPoints.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;
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700344 } else if (routeService.longestPrefixMatch(ipAddress) != null) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200345 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;
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700363 Route route = routeService.longestPrefixMatch(dstIpAddress);
364 if (route != null) {
365 nextHopIpAddress = route.nextHop();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200366 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 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700397}
398