blob: fbecd68bc3fba5114c68eede50bdf2c45671aeb2 [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 Hart1ad75f22016-04-13 21:24:13 -070022import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart41349e92015-02-09 14:14:02 -080024import org.apache.felix.scr.annotations.Service;
Jonathan Hartab63aac2014-10-16 08:52:55 -070025import org.jboss.netty.bootstrap.ServerBootstrap;
26import org.jboss.netty.channel.Channel;
27import org.jboss.netty.channel.ChannelException;
28import org.jboss.netty.channel.ChannelFactory;
29import org.jboss.netty.channel.ChannelPipeline;
30import org.jboss.netty.channel.ChannelPipelineFactory;
31import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080032import org.jboss.netty.channel.group.ChannelGroup;
33import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070034import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080035import org.onlab.packet.Ip4Address;
36import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080037import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080038import org.onlab.packet.IpPrefix;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070039import org.onosproject.incubator.net.routing.Route;
40import org.onosproject.incubator.net.routing.RouteAdminService;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080041import org.osgi.service.component.ComponentContext;
Jonathan Hartab63aac2014-10-16 08:52:55 -070042import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080045import java.net.InetAddress;
46import java.net.InetSocketAddress;
47import java.net.SocketAddress;
48import java.util.Collection;
Jonathan Hartd24fafb2015-02-09 17:55:32 -080049import java.util.Dictionary;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080050import java.util.concurrent.ConcurrentHashMap;
51import java.util.concurrent.ConcurrentMap;
52
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080053import static java.util.concurrent.Executors.newCachedThreadPool;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080054import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080055
Jonathan Hartab63aac2014-10-16 08:52:55 -070056/**
57 * BGP Session Manager class.
58 */
Jonathan Hartc22e8472015-11-17 18:25:45 -080059@Component(immediate = true, enabled = false)
Jonathan Hart41349e92015-02-09 14:14:02 -080060@Service
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
Jonathan Hart1ad75f22016-04-13 21:24:13 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected RouteAdminService routeService;
67
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080068 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070069 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080070 private ServerBootstrap serverBootstrap;
71 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070072 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080073 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080074 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070075
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080076 private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
77 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080078 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080079 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080080 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070081
Jonathan Hartd24fafb2015-02-09 17:55:32 -080082 private static final int DEFAULT_BGP_PORT = 2000;
83 private int bgpPort;
84
85 @Activate
86 protected void activate(ComponentContext context) {
87 readComponentConfiguration(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -070088 start();
Jonathan Hartd24fafb2015-02-09 17:55:32 -080089 log.info("BgpSessionManager started");
90 }
91
92 @Deactivate
93 protected void deactivate() {
Jonathan Hart1ad75f22016-04-13 21:24:13 -070094 stop();
Jonathan Hartd24fafb2015-02-09 17:55:32 -080095 log.info("BgpSessionManager stopped");
96 }
97
98 /**
99 * Extracts properties from the component configuration context.
100 *
101 * @param context the component context
102 */
103 private void readComponentConfiguration(ComponentContext context) {
104 Dictionary<?, ?> properties = context.getProperties();
105 try {
106 String strPort = (String) properties.get("bgpPort");
107 if (strPort != null) {
108 bgpPort = Integer.parseInt(strPort);
109 } else {
110 bgpPort = DEFAULT_BGP_PORT;
111 }
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800112 } catch (NumberFormatException | ClassCastException e) {
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800113 bgpPort = DEFAULT_BGP_PORT;
114 }
115 log.debug("BGP port is set to {}", bgpPort);
116 }
117
118 @Modified
119 public void modified(ComponentContext context) {
120 // Blank @Modified method to catch modifications to the context.
121 // If no @Modified method exists, it seems @Activate is called again
122 // when the context is modified.
123 }
124
Jonathan Hartab63aac2014-10-16 08:52:55 -0700125 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800126 * Checks whether the BGP Session Manager is shutdown.
127 *
128 * @return true if the BGP Session Manager is shutdown, otherwise false
129 */
130 boolean isShutdown() {
131 return this.isShutdown;
132 }
133
134 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700135 * Gets the BGP sessions.
136 *
137 * @return the BGP sessions
138 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700139 @Override
Jonathan Hartab63aac2014-10-16 08:52:55 -0700140 public Collection<BgpSession> getBgpSessions() {
141 return bgpSessions.values();
142 }
143
144 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800145 * Gets the selected IPv4 BGP routes among all BGP sessions.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700146 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800147 * @return the selected IPv4 BGP routes among all BGP sessions
Jonathan Hartab63aac2014-10-16 08:52:55 -0700148 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700149 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800150 public Collection<BgpRouteEntry> getBgpRoutes4() {
151 return bgpRoutes4.values();
152 }
153
154 /**
155 * Gets the selected IPv6 BGP routes among all BGP sessions.
156 *
157 * @return the selected IPv6 BGP routes among all BGP sessions
158 */
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700159 @Override
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800160 public Collection<BgpRouteEntry> getBgpRoutes6() {
161 return bgpRoutes6.values();
162 }
163
164 /**
165 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
166 *
167 * @param prefix the prefix to use
168 * @return the BGP route if found, otherwise null
169 */
170 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700171 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800172 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
173 }
174 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
175 }
176
177 /**
178 * Adds a BGP route. The route can be either IPv4 or IPv6.
179 *
180 * @param bgpRouteEntry the BGP route entry to use
181 */
182 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700183 if (bgpRouteEntry.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800184 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
185 bgpRouteEntry);
186 } else {
187 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
188 bgpRouteEntry);
189 }
190 }
191
192 /**
193 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
194 *
195 * @param prefix the prefix to use
196 * @return true if the route was found and removed, otherwise false
197 */
198 boolean removeBgpRoute(IpPrefix prefix) {
Pavlin Radoslavov87dd9302015-03-10 13:53:24 -0700199 if (prefix.isIp4()) {
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800200 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
201 }
202 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700203 }
204
205 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800206 * Adds the channel for a BGP session.
207 *
208 * @param channel the channel to add
209 */
210 void addSessionChannel(Channel channel) {
211 allChannels.add(channel);
212 }
213
214 /**
215 * Removes the channel for a BGP session.
216 *
217 * @param channel the channel to remove
218 */
219 void removeSessionChannel(Channel channel) {
220 allChannels.remove(channel);
221 }
222
223 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700224 * Processes the connection from a BGP peer.
225 *
226 * @param bgpSession the BGP session for the peer
227 * @return true if the connection can be established, otherwise false
228 */
229 boolean peerConnected(BgpSession bgpSession) {
230
231 // Test whether there is already a session from the same remote
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800232 if (bgpSessions.get(bgpSession.remoteInfo().address()) != null) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700233 return false; // Duplicate BGP session
234 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800235 bgpSessions.put(bgpSession.remoteInfo().address(), bgpSession);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700236
237 //
238 // If the first connection, set my BGP ID to the local address
239 // of the socket.
240 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800241 if (bgpSession.localInfo().address() instanceof InetSocketAddress) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700242 InetAddress inetAddr =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800243 ((InetSocketAddress) bgpSession.localInfo().address()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800244 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700245 updateMyBgpId(ip4Address);
246 }
247 return true;
248 }
249
250 /**
251 * Processes the disconnection from a BGP peer.
252 *
253 * @param bgpSession the BGP session for the peer
254 */
255 void peerDisconnected(BgpSession bgpSession) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800256 bgpSessions.remove(bgpSession.remoteInfo().address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700257 }
258
259 /**
260 * Conditionally updates the local BGP ID if it wasn't set already.
261 * <p/>
262 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
263 *
264 * @param ip4Address the IPv4 address to use as BGP ID
265 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800266 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700267 if (myBgpId == null) {
268 myBgpId = ip4Address;
269 log.debug("BGP: My BGP ID is {}", myBgpId);
270 }
271 }
272
273 /**
274 * Gets the local BGP Identifier as an IPv4 address.
275 *
276 * @return the local BGP Identifier as an IPv4 address
277 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800278 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700279 return myBgpId;
280 }
281
282 /**
283 * Gets the BGP Route Selector.
284 *
285 * @return the BGP Route Selector
286 */
287 BgpRouteSelector getBgpRouteSelector() {
288 return bgpRouteSelector;
289 }
290
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700291 /**
292 * Sends updates routes to the route service.
293 *
294 * @param updates routes to update
295 */
296 void update(Collection<Route> updates) {
297 routeService.update(updates);
298 }
299
300 /**
301 * Sends withdrawn routes to the routes service.
302 *
303 * @param withdraws routes to withdraw
304 */
305 void withdraw(Collection<Route> withdraws) {
306 routeService.withdraw(withdraws);
307 }
308
309
310 public void start() {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800311 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800312 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700313
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800314 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700315 newCachedThreadPool(groupedThreads("onos/bgp", "sm-boss-%d", log)),
316 newCachedThreadPool(groupedThreads("onos/bgp", "sm-worker-%d", log)));
Sho SHIMIZU74626412015-09-11 11:46:27 -0700317 ChannelPipelineFactory pipelineFactory = () -> {
318 // Allocate a new session per connection
319 BgpSession bgpSessionHandler =
320 new BgpSession(BgpSessionManager.this);
321 BgpFrameDecoder bgpFrameDecoder =
322 new BgpFrameDecoder(bgpSessionHandler);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700323
Sho SHIMIZU74626412015-09-11 11:46:27 -0700324 // Setup the processing pipeline
325 ChannelPipeline pipeline = Channels.pipeline();
326 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
327 pipeline.addLast("BgpSession", bgpSessionHandler);
328 return pipeline;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800329 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700330 InetSocketAddress listenAddress =
Jonathan Hartd24fafb2015-02-09 17:55:32 -0800331 new InetSocketAddress(bgpPort);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700332
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800333 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700334 // serverBootstrap.setOptions("reuseAddr", true);
335 serverBootstrap.setOption("child.keepAlive", true);
336 serverBootstrap.setOption("child.tcpNoDelay", true);
337 serverBootstrap.setPipelineFactory(pipelineFactory);
338 try {
339 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800340 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700341 } catch (ChannelException e) {
342 log.debug("Exception binding to BGP port {}: ",
343 listenAddress.getPort(), e);
344 }
345 }
346
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800347 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800348 isShutdown = true;
349 allChannels.close().awaitUninterruptibly();
350 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700351 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700352}