blob: 5c3c18e78d3cbe8223665bc3edb1b8520813d6fc [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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 Hart41349e92015-02-09 14:14:02 -080016package org.onosproject.routing.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070017
Jonathan Hartd24fafb2015-02-09 17:55:32 -080018import org.apache.felix.scr.annotations.Activate;
Jonathan Hart41349e92015-02-09 14:14:02 -080019import org.apache.felix.scr.annotations.Component;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080020import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Modified;
Jonathan Hart41349e92015-02-09 14:14:02 -080022import org.apache.felix.scr.annotations.Service;
Jonathan Hartab63aac2014-10-16 08:52:55 -070023import org.jboss.netty.bootstrap.ServerBootstrap;
24import org.jboss.netty.channel.Channel;
25import org.jboss.netty.channel.ChannelException;
26import org.jboss.netty.channel.ChannelFactory;
27import org.jboss.netty.channel.ChannelPipeline;
28import org.jboss.netty.channel.ChannelPipelineFactory;
29import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080030import org.jboss.netty.channel.group.ChannelGroup;
31import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070032import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080033import org.onlab.packet.Ip4Address;
34import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080035import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080036import org.onlab.packet.IpPrefix;
Jonathan Hart3930f632015-10-19 12:12:51 -070037import org.onosproject.routing.RouteSourceService;
Jonathan Hart2da1e602015-02-18 19:09:24 -080038import org.onosproject.routing.RouteListener;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080039import org.osgi.service.component.ComponentContext;
Jonathan Hartab63aac2014-10-16 08:52:55 -070040import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080043import java.net.InetAddress;
44import java.net.InetSocketAddress;
45import java.net.SocketAddress;
46import java.util.Collection;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080047import java.util.Dictionary;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080048import java.util.concurrent.ConcurrentHashMap;
49import java.util.concurrent.ConcurrentMap;
50
51import static com.google.common.base.Preconditions.checkNotNull;
52import static java.util.concurrent.Executors.newCachedThreadPool;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080053import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080054
Jonathan Hartab63aac2014-10-16 08:52:55 -070055/**
56 * BGP Session Manager class.
57 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080058@Component(immediate = true, enabled = false)
Jonathan Hart41349e92015-02-09 14:14:02 -080059@Service
Jonathan Hart3930f632015-10-19 12:12:51 -070060public class BgpSessionManager implements BgpInfoService, RouteSourceService {
Jonathan Hartab63aac2014-10-16 08:52:55 -070061 private static final Logger log =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080062 LoggerFactory.getLogger(BgpSessionManager.class);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080063
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080064 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070065 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080066 private ServerBootstrap serverBootstrap;
67 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070068 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080069 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080070 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070071
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080072 private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
73 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080074 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080075 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080076 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070077
Jonathan Hart41349e92015-02-09 14:14:02 -080078 private RouteListener routeListener;
Jonathan Hartab63aac2014-10-16 08:52:55 -070079
Jonathan Hartd24fafb2015-02-09 17:55:32 -080080 private static final int DEFAULT_BGP_PORT = 2000;
81 private int bgpPort;
82
83 @Activate
84 protected void activate(ComponentContext context) {
85 readComponentConfiguration(context);
86 log.info("BgpSessionManager started");
87 }
88
89 @Deactivate
90 protected void deactivate() {
91 log.info("BgpSessionManager stopped");
92 }
93
94 /**
95 * Extracts properties from the component configuration context.
96 *
97 * @param context the component context
98 */
99 private void readComponentConfiguration(ComponentContext context) {
100 Dictionary<?, ?> properties = context.getProperties();
101 try {
102 String strPort = (String) properties.get("bgpPort");
103 if (strPort != null) {
104 bgpPort = Integer.parseInt(strPort);
105 } else {
106 bgpPort = DEFAULT_BGP_PORT;
107 }
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800108 } catch (NumberFormatException | ClassCastException e) {
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800109 bgpPort = DEFAULT_BGP_PORT;
110 }
111 log.debug("BGP port is set to {}", bgpPort);
112 }
113
114 @Modified
115 public void modified(ComponentContext context) {
116 // Blank @Modified method to catch modifications to the context.
117 // If no @Modified method exists, it seems @Activate is called again
118 // when the context is modified.
119 }
120
Jonathan Hartab63aac2014-10-16 08:52:55 -0700121 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800122 * Checks whether the BGP Session Manager is shutdown.
123 *
124 * @return true if the BGP Session Manager is shutdown, otherwise false
125 */
126 boolean isShutdown() {
127 return this.isShutdown;
128 }
129
130 /**
131 * Gets the route listener.
132 *
133 * @return the route listener to use
134 */
135 RouteListener getRouteListener() {
136 return routeListener;
137 }
138
139 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700140 * Gets the BGP sessions.
141 *
142 * @return the BGP sessions
143 */
144 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 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800153 public Collection<BgpRouteEntry> getBgpRoutes4() {
154 return bgpRoutes4.values();
155 }
156
157 /**
158 * Gets the selected IPv6 BGP routes among all BGP sessions.
159 *
160 * @return the selected IPv6 BGP routes among all BGP sessions
161 */
162 public Collection<BgpRouteEntry> getBgpRoutes6() {
163 return bgpRoutes6.values();
164 }
165
166 /**
167 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
168 *
169 * @param prefix the prefix to use
170 * @return the BGP route if found, otherwise null
171 */
172 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700173 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800174 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
175 }
176 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
177 }
178
179 /**
180 * Adds a BGP route. The route can be either IPv4 or IPv6.
181 *
182 * @param bgpRouteEntry the BGP route entry to use
183 */
184 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700185 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800186 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
187 bgpRouteEntry);
188 } else {
189 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
190 bgpRouteEntry);
191 }
192 }
193
194 /**
195 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
196 *
197 * @param prefix the prefix to use
198 * @return true if the route was found and removed, otherwise false
199 */
200 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700201 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800202 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
203 }
204 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700205 }
206
207 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800208 * Adds the channel for a BGP session.
209 *
210 * @param channel the channel to add
211 */
212 void addSessionChannel(Channel channel) {
213 allChannels.add(channel);
214 }
215
216 /**
217 * Removes the channel for a BGP session.
218 *
219 * @param channel the channel to remove
220 */
221 void removeSessionChannel(Channel channel) {
222 allChannels.remove(channel);
223 }
224
225 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700226 * Processes the connection from a BGP peer.
227 *
228 * @param bgpSession the BGP session for the peer
229 * @return true if the connection can be established, otherwise false
230 */
231 boolean peerConnected(BgpSession bgpSession) {
232
233 // Test whether there is already a session from the same remote
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800234 if (bgpSessions.get(bgpSession.remoteInfo().address()) != null) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700235 return false; // Duplicate BGP session
236 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800237 bgpSessions.put(bgpSession.remoteInfo().address(), bgpSession);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700238
239 //
240 // If the first connection, set my BGP ID to the local address
241 // of the socket.
242 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800243 if (bgpSession.localInfo().address() instanceof InetSocketAddress) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700244 InetAddress inetAddr =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800245 ((InetSocketAddress) bgpSession.localInfo().address()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800246 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700247 updateMyBgpId(ip4Address);
248 }
249 return true;
250 }
251
252 /**
253 * Processes the disconnection from a BGP peer.
254 *
255 * @param bgpSession the BGP session for the peer
256 */
257 void peerDisconnected(BgpSession bgpSession) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800258 bgpSessions.remove(bgpSession.remoteInfo().address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700259 }
260
261 /**
262 * Conditionally updates the local BGP ID if it wasn't set already.
263 * <p/>
264 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
265 *
266 * @param ip4Address the IPv4 address to use as BGP ID
267 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800268 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700269 if (myBgpId == null) {
270 myBgpId = ip4Address;
271 log.debug("BGP: My BGP ID is {}", myBgpId);
272 }
273 }
274
275 /**
276 * Gets the local BGP Identifier as an IPv4 address.
277 *
278 * @return the local BGP Identifier as an IPv4 address
279 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800280 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700281 return myBgpId;
282 }
283
284 /**
285 * Gets the BGP Route Selector.
286 *
287 * @return the BGP Route Selector
288 */
289 BgpRouteSelector getBgpRouteSelector() {
290 return bgpRouteSelector;
291 }
292
Jonathan Hart41349e92015-02-09 14:14:02 -0800293 @Override
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800294 public void start(RouteListener routeListener) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800295 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800296 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700297
Jonathan Hart41349e92015-02-09 14:14:02 -0800298 this.routeListener = checkNotNull(routeListener);
299
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800300 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800301 newCachedThreadPool(groupedThreads("onos/bgp", "sm-boss-%d")),
302 newCachedThreadPool(groupedThreads("onos/bgp", "sm-worker-%d")));
Sho SHIMIZU74626412015-09-11 11:46:27 -0700303 ChannelPipelineFactory pipelineFactory = () -> {
304 // Allocate a new session per connection
305 BgpSession bgpSessionHandler =
306 new BgpSession(BgpSessionManager.this);
307 BgpFrameDecoder bgpFrameDecoder =
308 new BgpFrameDecoder(bgpSessionHandler);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700309
Sho SHIMIZU74626412015-09-11 11:46:27 -0700310 // Setup the processing pipeline
311 ChannelPipeline pipeline = Channels.pipeline();
312 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
313 pipeline.addLast("BgpSession", bgpSessionHandler);
314 return pipeline;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800315 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700316 InetSocketAddress listenAddress =
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800317 new InetSocketAddress(bgpPort);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700318
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800319 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700320 // serverBootstrap.setOptions("reuseAddr", true);
321 serverBootstrap.setOption("child.keepAlive", true);
322 serverBootstrap.setOption("child.tcpNoDelay", true);
323 serverBootstrap.setPipelineFactory(pipelineFactory);
324 try {
325 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800326 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700327 } catch (ChannelException e) {
328 log.debug("Exception binding to BGP port {}: ",
329 listenAddress.getPort(), e);
330 }
331 }
332
Jonathan Hart41349e92015-02-09 14:14:02 -0800333 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800334 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800335 isShutdown = true;
336 allChannels.close().awaitUninterruptibly();
337 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700338 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700339}