blob: 7a1fcec42c5bb8fe4fb81a1f05fa45228e2e593e [file] [log] [blame]
Himal Kumarb43724d2016-04-29 14:15:57 +10001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Himal Kumarb43724d2016-04-29 14:15:57 +10003 *
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.castor;
17import org.apache.felix.scr.annotations.Activate;
18import org.apache.felix.scr.annotations.Component;
19import org.apache.felix.scr.annotations.Deactivate;
20import org.apache.felix.scr.annotations.Reference;
21import org.apache.felix.scr.annotations.ReferenceCardinality;
22import org.apache.felix.scr.annotations.Service;
23import org.onlab.packet.Ethernet;
24import org.onlab.packet.IPv4;
25import org.onlab.packet.IPv6;
26import org.onlab.packet.IpAddress;
27import org.onlab.packet.IpPrefix;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.TpPort;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
37import org.onosproject.net.intent.Key;
38import org.onosproject.net.intent.MultiPointToSinglePointIntent;
39import org.onosproject.net.intent.PointToPointIntent;
Jonathan Hart470ed4f2017-01-31 16:52:28 -080040import org.onosproject.intentsync.IntentSynchronizationService;
Himal Kumarb43724d2016-04-29 14:15:57 +100041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44import java.util.ArrayList;
45import java.util.Collection;
46import java.util.HashMap;
47import java.util.HashSet;
48import java.util.LinkedList;
49import java.util.List;
50import java.util.Map;
51import java.util.Set;
52
53import static com.google.common.base.Preconditions.checkNotNull;
54
55/**
56 * Manages the connectivity requirements between peers.
57 */
58@Component(immediate = true, enabled = true)
59@Service
60public class ConnectivityManager implements ConnectivityManagerService {
61
62 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 protected IntentSynchronizationService intentSynchronizer;
64
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected CoreService coreService;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected CastorStore castorStore;
70
71 private static final int PRIORITY_OFFSET = 1000;
72 private static final int FLOW_PRIORITY = 500;
73 private static final String SUFFIX_DST = "dst";
74 private static final String SUFFIX_SRC = "src";
75 private static final String SUFFIX_ICMP = "icmp";
76
77 private static final Logger log = LoggerFactory.getLogger(ConnectivityManager.class);
78
79 private static final short BGP_PORT = 179;
80
81 private ApplicationId appId;
82
83 @Activate
84 public void activate() {
85 appId = coreService.getAppId(Castor.CASTOR_APP);
86 }
87
88 @Deactivate
89 public void deactivate() {
90 }
91
92 /**
93 * Inputs the Route Servers.
94 */
95 @Override
96 public void start(Peer server) {
97 //routeServers.add(server);
98 //allPeers.add(server);
99 castorStore.storePeer(server);
100 castorStore.storeServer(server);
101 }
102
103 /**
104 * Stops the peer connectivity manager.
105 */
106 public void stop() {};
107
108 /**
109 * Sets up paths to establish connectivity between all internal.
110 * BGP speakers and external BGP peers.
111 */
112 @Override
113 public void setUpConnectivity(Peer peer) {
114
115 if (!castorStore.getCustomers().contains(peer)) {
116 castorStore.storePeer(peer);
117 castorStore.storeCustomer(peer);
118 }
119
120 for (Peer routeServer : castorStore.getServers()) {
121 log.debug("Start to set up BGP paths for BGP peer and Route Server"
122 + peer + "&" + routeServer);
123
124 buildSpeakerIntents(routeServer, peer).forEach(i -> {
125 castorStore.storePeerIntent(i.key(), i);
126 intentSynchronizer.submit(i);
127 });
128 }
129 }
130
131 private Collection<PointToPointIntent> buildSpeakerIntents(Peer speaker, Peer peer) {
132 List<PointToPointIntent> intents = new ArrayList<>();
133
134 IpAddress peerAddress = IpAddress.valueOf(peer.getIpAddress());
135 IpAddress speakerAddress = IpAddress.valueOf(speaker.getIpAddress());
136
137 checkNotNull(peerAddress);
138
139 intents.addAll(buildIntents(ConnectPoint.deviceConnectPoint(speaker.getPort()), speakerAddress,
140 ConnectPoint.deviceConnectPoint(peer.getPort()), peerAddress));
141
142 return intents;
143 }
144
145 /**
146 * Builds the required intents between the two pairs of connect points and
147 * IP addresses.
148 *
149 * @param portOne the first connect point
150 * @param ipOne the first IP address
151 * @param portTwo the second connect point
152 * @param ipTwo the second IP address
153 * @return the intents to install
154 */
155 private Collection<PointToPointIntent> buildIntents(ConnectPoint portOne,
156 IpAddress ipOne,
157 ConnectPoint portTwo,
158 IpAddress ipTwo) {
159
160 List<PointToPointIntent> intents = new ArrayList<>();
161
162 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
163 TrafficSelector selector;
164 Key key;
165
166 byte tcpProtocol;
167 byte icmpProtocol;
168
169 if (ipOne.isIp4()) {
170 tcpProtocol = IPv4.PROTOCOL_TCP;
171 icmpProtocol = IPv4.PROTOCOL_ICMP;
172 } else {
173 tcpProtocol = IPv6.PROTOCOL_TCP;
174 icmpProtocol = IPv6.PROTOCOL_ICMP6;
175 }
176
177 // Path from BGP speaker to BGP peer matching source TCP port 179
178 selector = buildSelector(tcpProtocol,
179 ipOne,
180 ipTwo,
181 BGP_PORT,
182 null);
183
184 key = buildKey(ipOne, ipTwo, SUFFIX_SRC);
185
186 intents.add(PointToPointIntent.builder()
187 .appId(appId)
188 .key(key)
189 .selector(selector)
190 .treatment(treatment)
191 .ingressPoint(portOne)
192 .egressPoint(portTwo)
193 .priority(PRIORITY_OFFSET)
194 .build());
195
196 // Path from BGP peer to BGP speaker matching destination TCP port 179
197 selector = buildSelector(tcpProtocol,
198 ipTwo,
199 ipOne,
200 null,
201 BGP_PORT);
202
203 key = buildKey(ipTwo, ipOne, SUFFIX_DST);
204
205 intents.add(PointToPointIntent.builder()
206 .appId(appId)
207 .key(key)
208 .selector(selector)
209 .treatment(treatment)
210 .ingressPoint(portTwo)
211 .egressPoint(portOne)
212 .priority(PRIORITY_OFFSET)
213 .build());
214
215 // ICMP path from BGP speaker to BGP peer
216 selector = buildSelector(icmpProtocol,
217 ipOne,
218 ipTwo,
219 null,
220 null);
221
222 key = buildKey(ipOne, ipTwo, SUFFIX_ICMP);
223
224 intents.add(PointToPointIntent.builder()
225 .appId(appId)
226 .key(key)
227 .selector(selector)
228 .treatment(treatment)
229 .ingressPoint(portOne)
230 .egressPoint(portTwo)
231 .priority(PRIORITY_OFFSET)
232 .build());
233
234 // ICMP path from BGP peer to BGP speaker
235 selector = buildSelector(icmpProtocol,
236 ipTwo,
237 ipOne,
238 null,
239 null);
240
241 key = buildKey(ipTwo, ipOne, SUFFIX_ICMP);
242
243 intents.add(PointToPointIntent.builder()
244 .appId(appId)
245 .key(key)
246 .selector(selector)
247 .treatment(treatment)
248 .ingressPoint(portTwo)
249 .egressPoint(portOne)
250 .priority(PRIORITY_OFFSET)
251 .build());
252
253 return intents;
254 }
255
256 /**
257 * Builds a traffic selector based on the set of input parameters.
258 *
259 * @param ipProto IP protocol
260 * @param srcIp source IP address
261 * @param dstIp destination IP address
262 * @param srcTcpPort source TCP port, or null if shouldn't be set
263 * @param dstTcpPort destination TCP port, or null if shouldn't be set
264 * @return the new traffic selector
265 */
266 private TrafficSelector buildSelector(byte ipProto, IpAddress srcIp,
267 IpAddress dstIp, Short srcTcpPort,
268 Short dstTcpPort) {
269 TrafficSelector.Builder builder = DefaultTrafficSelector.builder().matchIPProtocol(ipProto);
270
271 if (dstIp.isIp4()) {
272 builder.matchEthType(Ethernet.TYPE_IPV4)
273 .matchIPSrc(IpPrefix.valueOf(srcIp, IpPrefix.MAX_INET_MASK_LENGTH))
274 .matchIPDst(IpPrefix.valueOf(dstIp, IpPrefix.MAX_INET_MASK_LENGTH));
275 } else {
276 builder.matchEthType(Ethernet.TYPE_IPV6)
277 .matchIPv6Src(IpPrefix.valueOf(srcIp, IpPrefix.MAX_INET6_MASK_LENGTH))
278 .matchIPv6Dst(IpPrefix.valueOf(dstIp, IpPrefix.MAX_INET6_MASK_LENGTH));
279 }
280
281 if (srcTcpPort != null) {
282 builder.matchTcpSrc(TpPort.tpPort(srcTcpPort));
283 }
284
285 if (dstTcpPort != null) {
286 builder.matchTcpDst(TpPort.tpPort(dstTcpPort));
287 }
288
289 return builder.build();
290 }
291
292 /**
293 * Builds an intent Key for a point-to-point intent based off the source
294 * and destination IP address, as well as a suffix String to distinguish
295 * between different types of intents between the same source and
296 * destination.
297 *
298 * @param srcIp source IP address
299 * @param dstIp destination IP address
300 * @param suffix suffix string
301 * @return intent key
302 */
303 private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) {
304 String keyString = new StringBuilder()
305 .append(srcIp.toString())
306 .append("-")
307 .append(dstIp.toString())
308 .append("-")
309 .append(suffix)
310 .toString();
311
312 return Key.of(keyString, appId);
313 }
314
315 @Override
316 public void setUpL2(Peer peer) {
317
318 // First update all the previous existing intents. Update ingress points.
319
320 if (!castorStore.getLayer2Intents().isEmpty()) {
321 updateExistingL2Intents(peer);
322 }
323
324 Set<ConnectPoint> ingressPorts = new HashSet<>();
325 ConnectPoint egressPort = ConnectPoint.deviceConnectPoint(peer.getPort());
326
327 for (Peer inPeer : castorStore.getAllPeers()) {
328 if (!inPeer.getName().equals(peer.getName())) {
329 ingressPorts.add(ConnectPoint.deviceConnectPoint(inPeer.getPort()));
330 }
331 }
332 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
333 MacAddress macAddress = castorStore.getAddressMap().get(IpAddress.valueOf(peer.getIpAddress()));
334 selector.matchEthDst(macAddress);
335 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
336 Key key = Key.of(peer.getIpAddress(), appId);
337
338 MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder()
339 .appId(appId)
340 .key(key)
341 .selector(selector.build())
342 .treatment(treatment)
343 .ingressPoints(ingressPorts)
344 .egressPoint(egressPort)
345 .priority(FLOW_PRIORITY)
346 .build();
347 intentSynchronizer.submit(intent);
348 castorStore.storeLayer2Intent(peer.getIpAddress(), intent);
349 castorStore.removeCustomer(peer);
350 peer.setL2(true);
351 castorStore.storeCustomer(peer);
352 }
353
354 /**
355 * Updates the existing layer 2 flows. Whenever a new Peer is added, it is also
356 * added as the ingress point to the existing layer two flows.
357 *
358 * @param peer The Peer being added.
359 */
360 private void updateExistingL2Intents(Peer peer) {
361
362 Collection<MultiPointToSinglePointIntent> oldIntents = castorStore.getLayer2Intents().values();
363
364 for (MultiPointToSinglePointIntent oldIntent : oldIntents) {
365
366 Set<ConnectPoint> ingressPoints = oldIntent.ingressPoints();
367 ConnectPoint egressPoint = oldIntent.egressPoint();
368 if (ingressPoints.add(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
369
370 MultiPointToSinglePointIntent updatedMp2pIntent =
371 MultiPointToSinglePointIntent.builder()
372 .appId(appId)
373 .key(oldIntent.key())
374 .selector(oldIntent.selector())
375 .treatment(oldIntent.treatment())
376 .ingressPoints(ingressPoints)
377 .egressPoint(egressPoint)
378 .priority(oldIntent.priority())
379 .build();
380
381 //layer2Intents.put(peer.getIpAddress(), updatedMp2pIntent);
382 castorStore.storeLayer2Intent(peer.getIpAddress(), updatedMp2pIntent);
383 intentSynchronizer.submit(updatedMp2pIntent);
384 }
385 }
386 }
387
388 @Override
389 public void deletePeer(Peer peer) {
390
391 if (castorStore.getCustomers().contains(peer)) {
392
393 deletel3(peer);
394
395 for (Peer customer : castorStore.getCustomers()) {
396 if (customer.getIpAddress().equals(peer.getIpAddress()) && customer.getl2Status()) {
397 deleteL2(customer);
398 updateL2AfterDeletion(customer);
399 }
400 }
401 castorStore.removeCustomer(peer);
402 }
403 }
404
405 /**
406 * Delete all the flows between the Peer being deleted and the Route Servers
407 * to kill the BGP sessions. It uses the keys to retrive the previous intents
408 * and withdraw them.
409 *
410 * @param peer The Peer being deleted.
411 */
412 private void deletel3(Peer peer) {
413
414 List<Key> keys = new LinkedList<>();
415 IpAddress ipTwo = IpAddress.valueOf(peer.getIpAddress());
416
417 for (Peer server : castorStore.getServers()) {
418 IpAddress ipOne = IpAddress.valueOf(server.getIpAddress());
419 keys.add(buildKey(ipOne, ipTwo, SUFFIX_SRC));
420 keys.add(buildKey(ipTwo, ipOne, SUFFIX_DST));
421 keys.add(buildKey(ipOne, ipTwo, SUFFIX_ICMP));
422 keys.add(buildKey(ipTwo, ipOne, SUFFIX_ICMP));
423 }
424 for (Key keyDel : keys) {
425
426 PointToPointIntent intent = castorStore.getPeerIntents().get(keyDel);
427 intentSynchronizer.withdraw(intent);
428 castorStore.removePeerIntent(keyDel);
429 }
430 }
431
432 /**
433 * Deletes the layer two flows for the peer being deleted.
434 *
435 * @param peer The Peer being deleted.
436 */
437 private void deleteL2(Peer peer) {
438 intentSynchronizer.withdraw(castorStore.getLayer2Intents().get(peer.getIpAddress()));
439 castorStore.removeLayer2Intent(peer.getIpAddress());
440 }
441
442 /**
443 * Updates all the layer 2 flows after successful deletion of a Peer.
444 * The Peer being deleted is removed from the ingress points of all
445 * other flows.
446 *
447 * @param peer The Peer being deleted.
448 */
449 private void updateL2AfterDeletion(Peer peer) {
450 Collection<MultiPointToSinglePointIntent> oldIntents = castorStore.getLayer2Intents().values();
451 Map<String, MultiPointToSinglePointIntent> intents = new HashMap<>();
452
453 for (MultiPointToSinglePointIntent oldIntent : oldIntents) {
454
455 Set<ConnectPoint> ingressPoints = oldIntent.ingressPoints();
456 ConnectPoint egressPoint = oldIntent.egressPoint();
457 if (ingressPoints.remove(ConnectPoint.deviceConnectPoint(peer.getPort()))) {
458
459 MultiPointToSinglePointIntent updatedMp2pIntent =
460 MultiPointToSinglePointIntent.builder()
461 .appId(appId)
462 .key(oldIntent.key())
463 .selector(oldIntent.selector())
464 .treatment(oldIntent.treatment())
465 .ingressPoints(ingressPoints)
466 .egressPoint(egressPoint)
467 .priority(oldIntent.priority())
468 .build();
469
470 intents.put(peer.getIpAddress(), updatedMp2pIntent);
471 intentSynchronizer.submit(updatedMp2pIntent);
472 }
473 }
474 for (String key : intents.keySet()) {
475 castorStore.storeLayer2Intent(key, intents.get(key));
476 }
477 }
478}