blob: 0259a2ffbfc4ed0fcfd692b6338ed81280ece021 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Jonathan Hartf4bd0482017-01-27 15:11:18 -080016
Jonathan Hart41349e92015-02-09 14:14:02 -080017package org.onosproject.routing.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070018
Jonathan Hartab63aac2014-10-16 08:52:55 -070019import org.jboss.netty.bootstrap.ServerBootstrap;
20import org.jboss.netty.channel.Channel;
21import org.jboss.netty.channel.ChannelException;
22import org.jboss.netty.channel.ChannelFactory;
23import org.jboss.netty.channel.ChannelPipeline;
24import org.jboss.netty.channel.ChannelPipelineFactory;
25import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080026import org.jboss.netty.channel.group.ChannelGroup;
27import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070028import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080029import org.onlab.packet.Ip4Address;
30import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080031import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080032import org.onlab.packet.IpPrefix;
Jonathan Hart10dbafd2017-05-18 15:53:03 -070033import org.onosproject.cluster.ClusterService;
Ray Milkey69ec8712017-08-08 13:00:43 -070034import org.onosproject.routeservice.Route;
35import org.onosproject.routeservice.RouteAdminService;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080036import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070037import org.osgi.service.component.annotations.Activate;
38import org.osgi.service.component.annotations.Component;
39import org.osgi.service.component.annotations.Deactivate;
40import org.osgi.service.component.annotations.Modified;
41import org.osgi.service.component.annotations.Reference;
42import org.osgi.service.component.annotations.ReferenceCardinality;
Jonathan Hartab63aac2014-10-16 08:52:55 -070043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080046import java.net.InetAddress;
47import java.net.InetSocketAddress;
48import java.net.SocketAddress;
49import java.util.Collection;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080050import java.util.Dictionary;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080051import java.util.concurrent.ConcurrentHashMap;
52import java.util.concurrent.ConcurrentMap;
53
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080054import static java.util.concurrent.Executors.newCachedThreadPool;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080055import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080056
Jonathan Hartab63aac2014-10-16 08:52:55 -070057/**
58 * BGP Session Manager class.
59 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070060@Component(immediate = true, service = BgpInfoService.class)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070061public class BgpSessionManager implements BgpInfoService {
Jonathan Hartab63aac2014-10-16 08:52:55 -070062 private static final Logger log =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080063 LoggerFactory.getLogger(BgpSessionManager.class);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080064
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070066 protected RouteAdminService routeService;
67
Ray Milkeyd84f89b2018-08-17 14:54:17 -070068 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart10dbafd2017-05-18 15:53:03 -070069 protected ClusterService clusterService;
70
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080071 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070072 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080073 private ServerBootstrap serverBootstrap;
74 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070075 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080076 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080077 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070078
Jonathan Hart10dbafd2017-05-18 15:53:03 -070079 private BgpRouteSelector bgpRouteSelector;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080080 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080081 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080082 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080083 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070084
Jonathan Hartd24fafb2015-02-09 17:55:32 -080085 private static final int DEFAULT_BGP_PORT = 2000;
86 private int bgpPort;
87
88 @Activate
89 protected void activate(ComponentContext context) {
Jonathan Hart10dbafd2017-05-18 15:53:03 -070090 bgpRouteSelector = new BgpRouteSelector(this, clusterService);
Jonathan Hartd24fafb2015-02-09 17:55:32 -080091 readComponentConfiguration(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -070092 start();
Jonathan Hartd24fafb2015-02-09 17:55:32 -080093 log.info("BgpSessionManager started");
94 }
95
96 @Deactivate
97 protected void deactivate() {
Jonathan Hart1ad75f22016-04-13 21:24:13 -070098 stop();
Jonathan Hartd24fafb2015-02-09 17:55:32 -080099 log.info("BgpSessionManager stopped");
100 }
101
102 /**
103 * Extracts properties from the component configuration context.
104 *
105 * @param context the component context
106 */
107 private void readComponentConfiguration(ComponentContext context) {
108 Dictionary<?, ?> properties = context.getProperties();
109 try {
110 String strPort = (String) properties.get("bgpPort");
111 if (strPort != null) {
112 bgpPort = Integer.parseInt(strPort);
113 } else {
114 bgpPort = DEFAULT_BGP_PORT;
115 }
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800116 } catch (NumberFormatException | ClassCastException e) {
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800117 bgpPort = DEFAULT_BGP_PORT;
118 }
119 log.debug("BGP port is set to {}", bgpPort);
120 }
121
122 @Modified
123 public void modified(ComponentContext context) {
124 // Blank @Modified method to catch modifications to the context.
125 // If no @Modified method exists, it seems @Activate is called again
126 // when the context is modified.
127 }
128
Jonathan Hartab63aac2014-10-16 08:52:55 -0700129 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800130 * Checks whether the BGP Session Manager is shutdown.
131 *
132 * @return true if the BGP Session Manager is shutdown, otherwise false
133 */
134 boolean isShutdown() {
135 return this.isShutdown;
136 }
137
138 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700139 * Gets the BGP sessions.
140 *
141 * @return the BGP sessions
142 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700143 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700144 public Collection<BgpSession> getBgpSessions() {
145 return bgpSessions.values();
146 }
147
148 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800149 * Gets the selected IPv4 BGP routes among all BGP sessions.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700150 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800151 * @return the selected IPv4 BGP routes among all BGP sessions
Jonathan Hartab63aac2014-10-16 08:52:55 -0700152 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700153 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800154 public Collection<BgpRouteEntry> getBgpRoutes4() {
155 return bgpRoutes4.values();
156 }
157
158 /**
159 * Gets the selected IPv6 BGP routes among all BGP sessions.
160 *
161 * @return the selected IPv6 BGP routes among all BGP sessions
162 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700163 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800164 public Collection<BgpRouteEntry> getBgpRoutes6() {
165 return bgpRoutes6.values();
166 }
167
168 /**
169 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
170 *
171 * @param prefix the prefix to use
172 * @return the BGP route if found, otherwise null
173 */
174 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700175 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800176 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
177 }
178 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
179 }
180
181 /**
182 * Adds a BGP route. The route can be either IPv4 or IPv6.
183 *
184 * @param bgpRouteEntry the BGP route entry to use
185 */
186 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700187 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800188 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
189 bgpRouteEntry);
190 } else {
191 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
192 bgpRouteEntry);
193 }
194 }
195
196 /**
197 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
198 *
199 * @param prefix the prefix to use
200 * @return true if the route was found and removed, otherwise false
201 */
202 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700203 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800204 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
205 }
206 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700207 }
208
209 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800210 * Adds the channel for a BGP session.
211 *
212 * @param channel the channel to add
213 */
214 void addSessionChannel(Channel channel) {
215 allChannels.add(channel);
216 }
217
218 /**
219 * Removes the channel for a BGP session.
220 *
221 * @param channel the channel to remove
222 */
223 void removeSessionChannel(Channel channel) {
224 allChannels.remove(channel);
225 }
226
227 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700228 * Processes the connection from a BGP peer.
229 *
230 * @param bgpSession the BGP session for the peer
231 * @return true if the connection can be established, otherwise false
232 */
233 boolean peerConnected(BgpSession bgpSession) {
234
235 // Test whether there is already a session from the same remote
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800236 if (bgpSessions.get(bgpSession.remoteInfo().address()) != null) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700237 return false; // Duplicate BGP session
238 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800239 bgpSessions.put(bgpSession.remoteInfo().address(), bgpSession);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700240
241 //
242 // If the first connection, set my BGP ID to the local address
243 // of the socket.
244 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800245 if (bgpSession.localInfo().address() instanceof InetSocketAddress) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700246 InetAddress inetAddr =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800247 ((InetSocketAddress) bgpSession.localInfo().address()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800248 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700249 updateMyBgpId(ip4Address);
250 }
251 return true;
252 }
253
254 /**
255 * Processes the disconnection from a BGP peer.
256 *
257 * @param bgpSession the BGP session for the peer
258 */
259 void peerDisconnected(BgpSession bgpSession) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800260 bgpSessions.remove(bgpSession.remoteInfo().address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700261 }
262
263 /**
264 * Conditionally updates the local BGP ID if it wasn't set already.
265 * <p/>
266 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
267 *
268 * @param ip4Address the IPv4 address to use as BGP ID
269 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800270 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700271 if (myBgpId == null) {
272 myBgpId = ip4Address;
273 log.debug("BGP: My BGP ID is {}", myBgpId);
274 }
275 }
276
277 /**
278 * Gets the local BGP Identifier as an IPv4 address.
279 *
280 * @return the local BGP Identifier as an IPv4 address
281 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800282 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700283 return myBgpId;
284 }
285
286 /**
287 * Gets the BGP Route Selector.
288 *
289 * @return the BGP Route Selector
290 */
291 BgpRouteSelector getBgpRouteSelector() {
292 return bgpRouteSelector;
293 }
294
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700295 /**
296 * Sends updates routes to the route service.
297 *
298 * @param updates routes to update
299 */
300 void update(Collection<Route> updates) {
301 routeService.update(updates);
302 }
303
304 /**
305 * Sends withdrawn routes to the routes service.
306 *
307 * @param withdraws routes to withdraw
308 */
309 void withdraw(Collection<Route> withdraws) {
310 routeService.withdraw(withdraws);
311 }
312
313
314 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800315 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800316 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700317
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800318 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700319 newCachedThreadPool(groupedThreads("onos/bgp", "sm-boss-%d", log)),
320 newCachedThreadPool(groupedThreads("onos/bgp", "sm-worker-%d", log)));
Sho SHIMIZU74626412015-09-11 11:46:27 -0700321 ChannelPipelineFactory pipelineFactory = () -> {
322 // Allocate a new session per connection
323 BgpSession bgpSessionHandler =
324 new BgpSession(BgpSessionManager.this);
325 BgpFrameDecoder bgpFrameDecoder =
326 new BgpFrameDecoder(bgpSessionHandler);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700327
Sho SHIMIZU74626412015-09-11 11:46:27 -0700328 // Setup the processing pipeline
329 ChannelPipeline pipeline = Channels.pipeline();
330 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
331 pipeline.addLast("BgpSession", bgpSessionHandler);
332 return pipeline;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800333 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700334 InetSocketAddress listenAddress =
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800335 new InetSocketAddress(bgpPort);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700336
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800337 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700338 // serverBootstrap.setOptions("reuseAddr", true);
339 serverBootstrap.setOption("child.keepAlive", true);
340 serverBootstrap.setOption("child.tcpNoDelay", true);
341 serverBootstrap.setPipelineFactory(pipelineFactory);
342 try {
343 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800344 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700345 } catch (ChannelException e) {
346 log.debug("Exception binding to BGP port {}: ",
347 listenAddress.getPort(), e);
348 }
349 }
350
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800351 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800352 isShutdown = true;
353 allChannels.close().awaitUninterruptibly();
354 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700355 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700356}