blob: a18e263c47a8ba90e1fc373c55978cd71b8c82a5 [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 Lin9a445c82016-04-07 11:40:29 -070018import static com.google.common.base.Preconditions.checkNotNull;
19import static org.onlab.packet.Ethernet.TYPE_ARP;
20import static org.onlab.packet.Ethernet.TYPE_IPV4;
21import static org.onosproject.net.packet.PacketPriority.REACTIVE;
22import static org.slf4j.LoggerFactory.getLogger;
23
24import java.nio.ByteBuffer;
25import java.util.Optional;
26import java.util.Set;
27
Pingping Line28ae4c2015-03-13 11:37:03 -070028import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
Pingping Linc9e16bf2015-04-10 14:42:41 -070033import org.onlab.packet.ARP;
Jonathan Hart3217d1b2015-06-30 16:08:43 -070034import org.onlab.packet.EthType;
Pingping Line28ae4c2015-03-13 11:37:03 -070035import org.onlab.packet.Ethernet;
36import org.onlab.packet.IPv4;
Pingping Linc9e16bf2015-04-10 14:42:41 -070037import org.onlab.packet.Ip4Address;
Jonathan Hart9a426f82015-09-03 15:43:13 +020038import org.onlab.packet.Ip6Address;
Pingping Line28ae4c2015-03-13 11:37:03 -070039import org.onlab.packet.IpAddress;
Jonathan Hart9a426f82015-09-03 15:43:13 +020040import org.onlab.packet.IpPrefix;
Pingping Line28ae4c2015-03-13 11:37:03 -070041import org.onlab.packet.MacAddress;
42import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020044import org.onosproject.incubator.net.intf.Interface;
45import org.onosproject.incubator.net.intf.InterfaceService;
Pingping Line28ae4c2015-03-13 11:37:03 -070046import org.onosproject.net.ConnectPoint;
Jonathan Hart9a426f82015-09-03 15:43:13 +020047import org.onosproject.net.Host;
Pingping Line28ae4c2015-03-13 11:37:03 -070048import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
50import org.onosproject.net.flow.TrafficSelector;
51import org.onosproject.net.flow.TrafficTreatment;
Jonathan Hart9a426f82015-09-03 15:43:13 +020052import org.onosproject.net.host.HostService;
Pingping Line28ae4c2015-03-13 11:37:03 -070053import org.onosproject.net.packet.DefaultOutboundPacket;
54import org.onosproject.net.packet.InboundPacket;
55import org.onosproject.net.packet.OutboundPacket;
56import org.onosproject.net.packet.PacketContext;
Pingping Line28ae4c2015-03-13 11:37:03 -070057import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020059import org.onosproject.routing.IntentRequestListener;
Jonathan Hart365335e2015-12-10 11:09:53 -080060import org.onosproject.routing.IntentSynchronizationService;
Jonathan Hart9a426f82015-09-03 15:43:13 +020061import org.onosproject.routing.RouteEntry;
Pingping Line28ae4c2015-03-13 11:37:03 -070062import org.onosproject.routing.RoutingService;
Pingping Linc9e16bf2015-04-10 14:42:41 -070063import org.onosproject.routing.config.RoutingConfigurationService;
Pingping Line28ae4c2015-03-13 11:37:03 -070064import org.slf4j.Logger;
65
66/**
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 intentRequestListener = new ReactiveRoutingFib(appId, hostService,
Pingping Lin9a445c82016-04-07 11:40:29 -0700111 interfaceService, intentSynchronizer);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200112
Brian O'Connor3b783262015-07-29 17:49:24 -0700113 packetService.addProcessor(processor, PacketProcessor.director(2));
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700114 requestIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700115 log.info("SDN-IP Reactive Routing Started");
116 }
117
118 @Deactivate
119 public void deactivate() {
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700120 withdrawIntercepts();
Pingping Line28ae4c2015-03-13 11:37:03 -0700121 packetService.removeProcessor(processor);
122 processor = null;
123 log.info("SDN-IP Reactive Routing Stopped");
124 }
125
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700126 /**
127 * Request packet in via the PacketService.
128 */
129 private void requestIntercepts() {
130 //TODO: to support IPv6 later
131 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
132 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700133 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700134 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700135 packetService.requestPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700136 }
137
138 /**
139 * Cancel request for packet in via PacketService.
140 */
141 private void withdrawIntercepts() {
142 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
143 selector.matchEthType(TYPE_IPV4);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700144 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700145 selector = DefaultTrafficSelector.builder();
146 selector.matchEthType(TYPE_ARP);
Thomas Vachuskac19d8292015-07-06 14:11:34 -0700147 packetService.cancelPackets(selector.build(), REACTIVE, appId);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700148 }
149
Pingping Line28ae4c2015-03-13 11:37:03 -0700150 private class ReactiveRoutingProcessor implements PacketProcessor {
151 @Override
152 public void process(PacketContext context) {
153
154 InboundPacket pkt = context.inPacket();
155 Ethernet ethPkt = pkt.parsed();
156 if (ethPkt == null) {
157 return;
158 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700159 ConnectPoint srcConnectPoint = pkt.receivedFrom();
Pingping Line28ae4c2015-03-13 11:37:03 -0700160
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700161 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
162 case ARP:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700163 ARP arpPacket = (ARP) ethPkt.getPayload();
164 Ip4Address targetIpAddress = Ip4Address
165 .valueOf(arpPacket.getTargetProtocolAddress());
166 // Only when it is an ARP request packet and the target IP
167 // address is a virtual gateway IP address, then it will be
168 // processed.
169 if (arpPacket.getOpCode() == ARP.OP_REQUEST
170 && config.isVirtualGatewayIpAddress(targetIpAddress)) {
171 MacAddress gatewayMacAddress =
172 config.getVirtualGatewayMacAddress();
173 if (gatewayMacAddress == null) {
174 break;
175 }
176 Ethernet eth = ARP.buildArpReply(targetIpAddress,
177 gatewayMacAddress,
178 ethPkt);
179
180 TrafficTreatment.Builder builder =
181 DefaultTrafficTreatment.builder();
182 builder.setOutput(srcConnectPoint.port());
183 packetService.emit(new DefaultOutboundPacket(
184 srcConnectPoint.deviceId(),
185 builder.build(),
186 ByteBuffer.wrap(eth.serialize())));
187 }
188 break;
Jonathan Hart3217d1b2015-06-30 16:08:43 -0700189 case IPV4:
Pingping Linc9e16bf2015-04-10 14:42:41 -0700190 // Parse packet
191 IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
192 IpAddress dstIp =
193 IpAddress.valueOf(ipv4Packet.getDestinationAddress());
194 IpAddress srcIp =
195 IpAddress.valueOf(ipv4Packet.getSourceAddress());
196 MacAddress srcMac = ethPkt.getSourceMAC();
Jonathan Hart9a426f82015-09-03 15:43:13 +0200197 packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700198
199 // TODO emit packet first or packetReactiveProcessor first
200 ConnectPoint egressConnectPoint = null;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200201 egressConnectPoint = getEgressConnectPoint(dstIp);
Pingping Linc9e16bf2015-04-10 14:42:41 -0700202 if (egressConnectPoint != null) {
203 forwardPacketToDst(context, egressConnectPoint);
204 }
205 break;
206 default:
207 break;
Pingping Line28ae4c2015-03-13 11:37:03 -0700208 }
209 }
210 }
211
212 /**
Jonathan Hart9a426f82015-09-03 15:43:13 +0200213 * Routes packet reactively.
214 *
215 * @param dstIpAddress the destination IP address of a packet
216 * @param srcIpAddress the source IP address of a packet
217 * @param srcConnectPoint the connect point where a packet comes from
218 * @param srcMacAddress the source MAC address of a packet
219 */
220 private void packetReactiveProcessor(IpAddress dstIpAddress,
221 IpAddress srcIpAddress,
222 ConnectPoint srcConnectPoint,
223 MacAddress srcMacAddress) {
224 checkNotNull(dstIpAddress);
225 checkNotNull(srcIpAddress);
226 checkNotNull(srcConnectPoint);
227 checkNotNull(srcMacAddress);
228
229 //
230 // Step1: Try to update the existing intent first if it exists.
231 //
232 IpPrefix ipPrefix = null;
233 RouteEntry routeEntry = null;
234 if (config.isIpAddressLocal(dstIpAddress)) {
235 if (dstIpAddress.isIp4()) {
236 ipPrefix = IpPrefix.valueOf(dstIpAddress,
237 Ip4Address.BIT_LENGTH);
238 } else {
239 ipPrefix = IpPrefix.valueOf(dstIpAddress,
240 Ip6Address.BIT_LENGTH);
241 }
242 } else {
243 // Get IP prefix from BGP route table
244 routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
245 if (routeEntry != null) {
246 ipPrefix = routeEntry.prefix();
247 }
248 }
249 if (ipPrefix != null
250 && intentRequestListener.mp2pIntentExists(ipPrefix)) {
251 intentRequestListener.updateExistingMp2pIntent(ipPrefix,
252 srcConnectPoint);
253 return;
254 }
255
256 //
257 // Step2: There is no existing intent for the destination IP address.
258 // Check whether it is necessary to create a new one. If necessary then
259 // create a new one.
260 //
261 TrafficType trafficType =
262 trafficTypeClassifier(srcConnectPoint, dstIpAddress);
263
264 switch (trafficType) {
265 case HOST_TO_INTERNET:
266 // If the destination IP address is outside the local SDN network.
267 // The Step 1 has already handled it. We do not need to do anything here.
268 intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress,
269 ipPrefix, routeEntry.nextHop());
270 break;
271 case INTERNET_TO_HOST:
272 intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress);
273 break;
274 case HOST_TO_HOST:
275 intentRequestListener.setUpConnectivityHostToHost(dstIpAddress,
276 srcIpAddress, srcMacAddress, srcConnectPoint);
277 break;
278 case INTERNET_TO_INTERNET:
279 log.trace("This is transit traffic, "
280 + "the intent should be preinstalled already");
281 break;
282 case DROP:
283 // TODO here should setUpDropPacketIntent(...);
284 // We need a new type of intent here.
285 break;
286 case UNKNOWN:
287 log.trace("This is unknown traffic, so we do nothing");
288 break;
289 default:
290 break;
291 }
292 }
293
294 /**
295 * Classifies the traffic and return the traffic type.
296 *
297 * @param srcConnectPoint the connect point where the packet comes from
298 * @param dstIp the destination IP address in packet
299 * @return the traffic type which this packet belongs to
300 */
301 private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint,
Pingping Lin9a445c82016-04-07 11:40:29 -0700302 IpAddress dstIp) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200303 LocationType dstIpLocationType = getLocationType(dstIp);
304 Optional<Interface> srcInterface =
305 interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst();
Pingping Lin9a445c82016-04-07 11:40:29 -0700306
307 Set<ConnectPoint> bgpPeerConnectPoints = config.getBgpPeerConnectPoints();
308
309
Jonathan Hart9a426f82015-09-03 15:43:13 +0200310
311 switch (dstIpLocationType) {
312 case INTERNET:
Pingping Lin9b85c032015-10-05 18:16:27 -0700313 if (srcInterface.isPresent() &&
Pingping Lin9a445c82016-04-07 11:40:29 -0700314 (!bgpPeerConnectPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200315 return TrafficType.HOST_TO_INTERNET;
316 } else {
317 return TrafficType.INTERNET_TO_INTERNET;
318 }
319 case LOCAL:
Pingping Lin9b85c032015-10-05 18:16:27 -0700320 if (srcInterface.isPresent() &&
Pingping Lin9a445c82016-04-07 11:40:29 -0700321 (!bgpPeerConnectPoints.contains(srcConnectPoint))) {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200322 return TrafficType.HOST_TO_HOST;
323 } else {
324 // TODO Currently we only consider local public prefixes.
325 // In the future, we will consider the local private prefixes.
326 // If dstIpLocationType is a local private, we should return
327 // TrafficType.DROP.
328 return TrafficType.INTERNET_TO_HOST;
329 }
330 case NO_ROUTE:
331 return TrafficType.DROP;
332 default:
333 return TrafficType.UNKNOWN;
334 }
335 }
336
337 /**
338 * Evaluates the location of an IP address and returns the location type.
339 *
340 * @param ipAddress the IP address to evaluate
341 * @return the IP address location type
342 */
343 private LocationType getLocationType(IpAddress ipAddress) {
344 if (config.isIpAddressLocal(ipAddress)) {
345 return LocationType.LOCAL;
346 } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) {
347 return LocationType.INTERNET;
348 } else {
349 return LocationType.NO_ROUTE;
350 }
351 }
352
353 public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) {
354 LocationType type = getLocationType(dstIpAddress);
355 if (type == LocationType.LOCAL) {
356 Set<Host> hosts = hostService.getHostsByIp(dstIpAddress);
357 if (!hosts.isEmpty()) {
358 return hosts.iterator().next().location();
359 } else {
360 hostService.startMonitoringIp(dstIpAddress);
361 return null;
362 }
363 } else if (type == LocationType.INTERNET) {
364 IpAddress nextHopIpAddress = null;
365 RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress);
366 if (routeEntry != null) {
367 nextHopIpAddress = routeEntry.nextHop();
368 Interface it = interfaceService.getMatchingInterface(nextHopIpAddress);
369 if (it != null) {
370 return it.connectPoint();
371 } else {
372 return null;
373 }
374 } else {
375 return null;
376 }
377 } else {
378 return null;
379 }
380 }
381
382 /**
Pingping Line28ae4c2015-03-13 11:37:03 -0700383 * Emits the specified packet onto the network.
384 *
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700385 * @param context the packet context
Pingping Line28ae4c2015-03-13 11:37:03 -0700386 * @param connectPoint the connect point where the packet should be
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700387 * sent out
Pingping Line28ae4c2015-03-13 11:37:03 -0700388 */
389 private void forwardPacketToDst(PacketContext context,
390 ConnectPoint connectPoint) {
391 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
392 .setOutput(connectPoint.port()).build();
393 OutboundPacket packet =
394 new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
395 context.inPacket().unparsed());
396 packetService.emit(packet);
397 log.trace("sending packet: {}", packet);
398 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700399}
400