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