blob: a2521ba1ca9a7320359c33ab3c39dee27621287a [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 Hartd24fafb2015-02-09 17:55:32 -080019import org.apache.felix.scr.annotations.Activate;
Jonathan Hart41349e92015-02-09 14:14:02 -080020import org.apache.felix.scr.annotations.Component;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080021import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Modified;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart41349e92015-02-09 14:14:02 -080025import org.apache.felix.scr.annotations.Service;
Jonathan Hartab63aac2014-10-16 08:52:55 -070026import org.jboss.netty.bootstrap.ServerBootstrap;
27import org.jboss.netty.channel.Channel;
28import org.jboss.netty.channel.ChannelException;
29import org.jboss.netty.channel.ChannelFactory;
30import org.jboss.netty.channel.ChannelPipeline;
31import org.jboss.netty.channel.ChannelPipelineFactory;
32import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080033import org.jboss.netty.channel.group.ChannelGroup;
34import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070035import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080036import org.onlab.packet.Ip4Address;
37import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080038import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080039import org.onlab.packet.IpPrefix;
Jonathan Hart10dbafd2017-05-18 15:53:03 -070040import org.onosproject.cluster.ClusterService;
Ray Milkey69ec8712017-08-08 13:00:43 -070041import org.onosproject.routeservice.Route;
42import org.onosproject.routeservice.RouteAdminService;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080043import org.osgi.service.component.ComponentContext;
Jonathan Hartab63aac2014-10-16 08:52:55 -070044import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080047import java.net.InetAddress;
48import java.net.InetSocketAddress;
49import java.net.SocketAddress;
50import java.util.Collection;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080051import java.util.Dictionary;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080052import java.util.concurrent.ConcurrentHashMap;
53import java.util.concurrent.ConcurrentMap;
54
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080055import static java.util.concurrent.Executors.newCachedThreadPool;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080056import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080057
Jonathan Hartab63aac2014-10-16 08:52:55 -070058/**
59 * BGP Session Manager class.
60 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080061@Component(immediate = true, enabled = false)
Jonathan Hart41349e92015-02-09 14:14:02 -080062@Service
Jonathan Hart1ad75f22016-04-13 21:24:13 -070063public class BgpSessionManager implements BgpInfoService {
Jonathan Hartab63aac2014-10-16 08:52:55 -070064 private static final Logger log =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080065 LoggerFactory.getLogger(BgpSessionManager.class);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080066
Jonathan Hart1ad75f22016-04-13 21:24:13 -070067 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected RouteAdminService routeService;
69
Jonathan Hart10dbafd2017-05-18 15:53:03 -070070 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected ClusterService clusterService;
72
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080073 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070074 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080075 private ServerBootstrap serverBootstrap;
76 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070077 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080078 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080079 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070080
Jonathan Hart10dbafd2017-05-18 15:53:03 -070081 private BgpRouteSelector bgpRouteSelector;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080082 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080083 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080084 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080085 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070086
Jonathan Hartd24fafb2015-02-09 17:55:32 -080087 private static final int DEFAULT_BGP_PORT = 2000;
88 private int bgpPort;
89
90 @Activate
91 protected void activate(ComponentContext context) {
Jonathan Hart10dbafd2017-05-18 15:53:03 -070092 bgpRouteSelector = new BgpRouteSelector(this, clusterService);
Jonathan Hartd24fafb2015-02-09 17:55:32 -080093 readComponentConfiguration(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -070094 start();
Jonathan Hartd24fafb2015-02-09 17:55:32 -080095 log.info("BgpSessionManager started");
96 }
97
98 @Deactivate
99 protected void deactivate() {
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700100 stop();
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800101 log.info("BgpSessionManager stopped");
102 }
103
104 /**
105 * Extracts properties from the component configuration context.
106 *
107 * @param context the component context
108 */
109 private void readComponentConfiguration(ComponentContext context) {
110 Dictionary<?, ?> properties = context.getProperties();
111 try {
112 String strPort = (String) properties.get("bgpPort");
113 if (strPort != null) {
114 bgpPort = Integer.parseInt(strPort);
115 } else {
116 bgpPort = DEFAULT_BGP_PORT;
117 }
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800118 } catch (NumberFormatException | ClassCastException e) {
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800119 bgpPort = DEFAULT_BGP_PORT;
120 }
121 log.debug("BGP port is set to {}", bgpPort);
122 }
123
124 @Modified
125 public void modified(ComponentContext context) {
126 // Blank @Modified method to catch modifications to the context.
127 // If no @Modified method exists, it seems @Activate is called again
128 // when the context is modified.
129 }
130
Jonathan Hartab63aac2014-10-16 08:52:55 -0700131 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800132 * Checks whether the BGP Session Manager is shutdown.
133 *
134 * @return true if the BGP Session Manager is shutdown, otherwise false
135 */
136 boolean isShutdown() {
137 return this.isShutdown;
138 }
139
140 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700141 * Gets the BGP sessions.
142 *
143 * @return the BGP sessions
144 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700145 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700146 public Collection<BgpSession> getBgpSessions() {
147 return bgpSessions.values();
148 }
149
150 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800151 * Gets the selected IPv4 BGP routes among all BGP sessions.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700152 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800153 * @return the selected IPv4 BGP routes among all BGP sessions
Jonathan Hartab63aac2014-10-16 08:52:55 -0700154 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700155 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800156 public Collection<BgpRouteEntry> getBgpRoutes4() {
157 return bgpRoutes4.values();
158 }
159
160 /**
161 * Gets the selected IPv6 BGP routes among all BGP sessions.
162 *
163 * @return the selected IPv6 BGP routes among all BGP sessions
164 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700165 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800166 public Collection<BgpRouteEntry> getBgpRoutes6() {
167 return bgpRoutes6.values();
168 }
169
170 /**
171 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
172 *
173 * @param prefix the prefix to use
174 * @return the BGP route if found, otherwise null
175 */
176 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700177 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800178 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
179 }
180 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
181 }
182
183 /**
184 * Adds a BGP route. The route can be either IPv4 or IPv6.
185 *
186 * @param bgpRouteEntry the BGP route entry to use
187 */
188 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700189 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800190 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
191 bgpRouteEntry);
192 } else {
193 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
194 bgpRouteEntry);
195 }
196 }
197
198 /**
199 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
200 *
201 * @param prefix the prefix to use
202 * @return true if the route was found and removed, otherwise false
203 */
204 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700205 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800206 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
207 }
208 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700209 }
210
211 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800212 * Adds the channel for a BGP session.
213 *
214 * @param channel the channel to add
215 */
216 void addSessionChannel(Channel channel) {
217 allChannels.add(channel);
218 }
219
220 /**
221 * Removes the channel for a BGP session.
222 *
223 * @param channel the channel to remove
224 */
225 void removeSessionChannel(Channel channel) {
226 allChannels.remove(channel);
227 }
228
229 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700230 * Processes the connection from a BGP peer.
231 *
232 * @param bgpSession the BGP session for the peer
233 * @return true if the connection can be established, otherwise false
234 */
235 boolean peerConnected(BgpSession bgpSession) {
236
237 // Test whether there is already a session from the same remote
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800238 if (bgpSessions.get(bgpSession.remoteInfo().address()) != null) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700239 return false; // Duplicate BGP session
240 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800241 bgpSessions.put(bgpSession.remoteInfo().address(), bgpSession);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700242
243 //
244 // If the first connection, set my BGP ID to the local address
245 // of the socket.
246 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800247 if (bgpSession.localInfo().address() instanceof InetSocketAddress) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700248 InetAddress inetAddr =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800249 ((InetSocketAddress) bgpSession.localInfo().address()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800250 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700251 updateMyBgpId(ip4Address);
252 }
253 return true;
254 }
255
256 /**
257 * Processes the disconnection from a BGP peer.
258 *
259 * @param bgpSession the BGP session for the peer
260 */
261 void peerDisconnected(BgpSession bgpSession) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800262 bgpSessions.remove(bgpSession.remoteInfo().address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700263 }
264
265 /**
266 * Conditionally updates the local BGP ID if it wasn't set already.
267 * <p/>
268 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
269 *
270 * @param ip4Address the IPv4 address to use as BGP ID
271 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800272 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700273 if (myBgpId == null) {
274 myBgpId = ip4Address;
275 log.debug("BGP: My BGP ID is {}", myBgpId);
276 }
277 }
278
279 /**
280 * Gets the local BGP Identifier as an IPv4 address.
281 *
282 * @return the local BGP Identifier as an IPv4 address
283 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800284 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700285 return myBgpId;
286 }
287
288 /**
289 * Gets the BGP Route Selector.
290 *
291 * @return the BGP Route Selector
292 */
293 BgpRouteSelector getBgpRouteSelector() {
294 return bgpRouteSelector;
295 }
296
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700297 /**
298 * Sends updates routes to the route service.
299 *
300 * @param updates routes to update
301 */
302 void update(Collection<Route> updates) {
303 routeService.update(updates);
304 }
305
306 /**
307 * Sends withdrawn routes to the routes service.
308 *
309 * @param withdraws routes to withdraw
310 */
311 void withdraw(Collection<Route> withdraws) {
312 routeService.withdraw(withdraws);
313 }
314
315
316 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800317 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800318 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700319
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800320 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700321 newCachedThreadPool(groupedThreads("onos/bgp", "sm-boss-%d", log)),
322 newCachedThreadPool(groupedThreads("onos/bgp", "sm-worker-%d", log)));
Sho SHIMIZU74626412015-09-11 11:46:27 -0700323 ChannelPipelineFactory pipelineFactory = () -> {
324 // Allocate a new session per connection
325 BgpSession bgpSessionHandler =
326 new BgpSession(BgpSessionManager.this);
327 BgpFrameDecoder bgpFrameDecoder =
328 new BgpFrameDecoder(bgpSessionHandler);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700329
Sho SHIMIZU74626412015-09-11 11:46:27 -0700330 // Setup the processing pipeline
331 ChannelPipeline pipeline = Channels.pipeline();
332 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
333 pipeline.addLast("BgpSession", bgpSessionHandler);
334 return pipeline;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800335 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700336 InetSocketAddress listenAddress =
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800337 new InetSocketAddress(bgpPort);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700338
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800339 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700340 // serverBootstrap.setOptions("reuseAddr", true);
341 serverBootstrap.setOption("child.keepAlive", true);
342 serverBootstrap.setOption("child.tcpNoDelay", true);
343 serverBootstrap.setPipelineFactory(pipelineFactory);
344 try {
345 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800346 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700347 } catch (ChannelException e) {
348 log.debug("Exception binding to BGP port {}: ",
349 listenAddress.getPort(), e);
350 }
351 }
352
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800353 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800354 isShutdown = true;
355 allChannels.close().awaitUninterruptibly();
356 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700357 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700358}