blob: 96aa06eefc53e94ba8fc9b71bc01fedafd61111f [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;
50import org.onosproject.routing.RouteEntry;
Pingping Line28ae4c2015-03-13 11:37:03 -070051import org.onosproject.routing.RoutingService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020052import org.onosproject.routing.SdnIpService;
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 Hart9a426f82015-09-03 15:43:13 +020089 protected SdnIpService sdnIpService;
90
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,
112 config, interfaceService,
113 sdnIpService.getIntentSynchronizationService());
114
Brian O'Connor3b783262015-07-29 17:49:24 -0700115 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700116 requestIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700117 log.info("SDN-IP Reactive Routing Started");
118 }
119
120 @Deactivate
121 public void deactivate() {
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700122 withdrawIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700123 packetService.removeProcessor(processor);
124 processor = null;
125 log.info("SDN-IP Reactive Routing Stopped");
126 }
127
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700128 /**
129 * Request packet in via the PacketService.
130 */
131 private void requestIntercepts() {
132 //TODO: to support IPv6 later
133 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
134 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700135 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700136 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700137 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700138 }
139
140 /**
141 * Cancel request for packet in via PacketService.
142 */
143 private void withdrawIntercepts() {
144 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
145 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700146 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700147 selector = DefaultTrafficSelector.builder();
148 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700149 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700150 }
151
Pingping Line28ae4c2015-03-13 11:37:03 -0700152 private class ReactiveRoutingProcessor implements PacketProcessor {
153 @Override
154 public void process(PacketContext context) {
155
156 InboundPacket pkt = context.inPacket();
157 Ethernet ethPkt = pkt.parsed();
158 if (ethPkt == null) {
159 return;
160 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700161 ConnectPoint srcConnectPoint = pkt.receivedFrom();
Pingping Line28ae4c2015-03-13 11:37:03 -0700162
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700163 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
164 case ARP:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700165 ARP arpPacket = (ARP) ethPkt.getPayload();
166 Ip4Address targetIpAddress = Ip4Address
167 .valueOf(arpPacket.getTargetProtocolAddress());
168 // Only when it is an ARP request packet and the target IP
169 // address is a virtual gateway IP address, then it will be
170 // processed.
171 if (arpPacket.getOpCode() == ARP.OP_REQUEST
172 && config.isVirtualGatewayIpAddress(targetIpAddress)) {
173 MacAddress gatewayMacAddress =
174 config.getVirtualGatewayMacAddress();
175 if (gatewayMacAddress == null) {
176 break;
177 }
178 Ethernet eth = ARP.buildArpReply(targetIpAddress,
179 gatewayMacAddress,
180 ethPkt);
181
182 TrafficTreatment.Builder builder =
183 DefaultTrafficTreatment.builder();
184 builder.setOutput(srcConnectPoint.port());
185 packetService.emit(new DefaultOutboundPacket(
186 srcConnectPoint.deviceId(),
187 builder.build(),
188 ByteBuffer.wrap(eth.serialize())));
189 }
190 break;
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700191 case IPV4:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700192 // Parse packet
193 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
194 IpAddress dstIp =
195 IpAddress.valueOf(ipv4Packet.getDestinationAddress());
196 IpAddress srcIp =
197 IpAddress.valueOf(ipv4Packet.getSourceAddress());
198 MacAddress srcMac = ethPkt.getSourceMAC();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200199 packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700200
201 // TODO emit packet first or packetReactiveProcessor first
202 ConnectPoint egressConnectPoint = null;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200203 egressConnectPoint = getEgressConnectPoint(dstIp);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700204 if (egressConnectPoint != null) {
205 forwardPacketToDst(context, egressConnectPoint);
206 }
207 break;
208 default:
209 break;
Pingping Line28ae4c2015-03-13 11:37:03 -0700210 }
211 }
212 }
213
214 /**
Jonathan Hart9a426f82015-09-03 15:43:13 +0200215 * Routes packet reactively.
216 *
217 * @param dstIpAddress the destination IP address of a packet
218 * @param srcIpAddress the source IP address of a packet
219 * @param srcConnectPoint the connect point where a packet comes from
220 * @param srcMacAddress the source MAC address of a packet
221 */
222 private void packetReactiveProcessor(IpAddress dstIpAddress,
223 IpAddress srcIpAddress,
224 ConnectPoint srcConnectPoint,
225 MacAddress srcMacAddress) {
226 checkNotNull(dstIpAddress);
227 checkNotNull(srcIpAddress);
228 checkNotNull(srcConnectPoint);
229 checkNotNull(srcMacAddress);
230
231 //
232 // Step1: Try to update the existing intent first if it exists.
233 //
234 IpPrefix ipPrefix = null;
235 RouteEntry routeEntry = null;
236 if (config.isIpAddressLocal(dstIpAddress)) {
237 if (dstIpAddress.isIp4()) {
238 ipPrefix = IpPrefix.valueOf(dstIpAddress,
239 Ip4Address.BIT_LENGTH);
240 } else {
241 ipPrefix = IpPrefix.valueOf(dstIpAddress,
242 Ip6Address.BIT_LENGTH);
243 }
244 } else {
245 // Get IP prefix from BGP route table
246 routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
247 if (routeEntry != null) {
248 ipPrefix = routeEntry.prefix();
249 }
250 }
251 if (ipPrefix != null
252 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
253 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
254 srcConnectPoint);
255 return;
256 }
257
258 //
259 // Step2: There is no existing intent for the destination IP address.
260 // Check whether it is necessary to create a new one. If necessary then
261 // create a new one.
262 //
263 TrafficType trafficType =
264 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
265
266 switch (trafficType) {
267 case HOST_TO_INTERNET:
268 // If the destination IP address is outside the local SDN network.
269 // The Step 1 has already handled it. We do not need to do anything here.
270 intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
271 ipPrefix, routeEntry.nextHop());
272 break;
273 case INTERNET_TO_HOST:
274 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
275 break;
276 case HOST_TO_HOST:
277 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
278 srcIpAddress, srcMacAddress, srcConnectPoint);
279 break;
280 case INTERNET_TO_INTERNET:
281 log.trace("This is transit traffic, "
282 + "the intent should be preinstalled already");
283 break;
284 case DROP:
285 // TODO here should setUpDropPacketIntent(...);
286 // We need a new type of intent here.
287 break;
288 case UNKNOWN:
289 log.trace("This is unknown traffic, so we do nothing");
290 break;
291 default:
292 break;
293 }
294 }
295
296 /**
297 * Classifies the traffic and return the traffic type.
298 *
299 * @param srcConnectPoint the connect point where the packet comes from
300 * @param dstIp the destination IP address in packet
301 * @return the traffic type which this packet belongs to
302 */
303 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
304 IpAddress dstIp) {
305 LocationType dstIpLocationType = getLocationType(dstIp);
306 Optional<Interface> srcInterface =
307 interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
Pingping Lin9b85c032015-10-05 18:16:27 -0700308 Set<ConnectPoint> ingressPoints = config.getBgpPeerConnectPoints();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200309
310 switch (dstIpLocationType) {
311 case INTERNET:
Pingping Lin9b85c032015-10-05 18:16:27 -0700312 if (srcInterface.isPresent() &&
313 (!ingressPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200314 return TrafficType.HOST_TO_INTERNET;
315 } else {
316 return TrafficType.INTERNET_TO_INTERNET;
317 }
318 case LOCAL:
Pingping Lin9b85c032015-10-05 18:16:27 -0700319 if (srcInterface.isPresent() &&
320 (!ingressPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200321 return TrafficType.HOST_TO_HOST;
322 } else {
323 // TODO Currently we only consider local public prefixes.
324 // In the future, we will consider the local private prefixes.
325 // If dstIpLocationType is a local private, we should return
326 // TrafficType.DROP.
327 return TrafficType.INTERNET_TO_HOST;
328 }
329 case NO_ROUTE:
330 return TrafficType.DROP;
331 default:
332 return TrafficType.UNKNOWN;
333 }
334 }
335
336 /**
337 * Evaluates the location of an IP address and returns the location type.
338 *
339 * @param ipAddress the IP address to evaluate
340 * @return the IP address location type
341 */
342 private LocationType getLocationType(IpAddress ipAddress) {
343 if (config.isIpAddressLocal(ipAddress)) {
344 return LocationType.LOCAL;
345 } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) {
346 return LocationType.INTERNET;
347 } else {
348 return LocationType.NO_ROUTE;
349 }
350 }
351
352 public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
353 LocationType type = getLocationType(dstIpAddress);
354 if (type == LocationType.LOCAL) {
355 Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
356 if (!hosts.isEmpty()) {
357 return hosts.iterator().next().location();
358 } else {
359 hostService.startMonitoringIp(dstIpAddress);
360 return null;
361 }
362 } else if (type == LocationType.INTERNET) {
363 IpAddress nextHopIpAddress = null;
364 RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
365 if (routeEntry != null) {
366 nextHopIpAddress = routeEntry.nextHop();
367 Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
368 if (it != null) {
369 return it.connectPoint();
370 } else {
371 return null;
372 }
373 } else {
374 return null;
375 }
376 } else {
377 return null;
378 }
379 }
380
381 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700382 * Emits the specified packet onto the network.
383 *
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700384 * @param context the packet context
Pingping Line28ae4c2015-03-13 11:37:03 -0700385 * @param connectPoint the connect point where the packet should be
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700386 * sent out
Pingping Line28ae4c2015-03-13 11:37:03 -0700387 */
388 private void forwardPacketToDst(PacketContext context,
389 ConnectPoint connectPoint) {
390 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
391 .setOutput(connectPoint.port()).build();
392 OutboundPacket packet =
393 new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
394 context.inPacket().unparsed());
395 packetService.emit(packet);
396 log.trace("sending packet: {}", packet);
397 }
398
399}
400