blob: cfd0081739fcfb08097624db51af0792640969f0 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 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 Hart41349e92015-02-09 14:14:02 -080018import org.apache.felix.scr.annotations.Component;
19import org.apache.felix.scr.annotations.Service;
Jonathan Hartab63aac2014-10-16 08:52:55 -070020import org.jboss.netty.bootstrap.ServerBootstrap;
21import org.jboss.netty.channel.Channel;
22import org.jboss.netty.channel.ChannelException;
23import org.jboss.netty.channel.ChannelFactory;
24import org.jboss.netty.channel.ChannelPipeline;
25import org.jboss.netty.channel.ChannelPipelineFactory;
26import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080027import org.jboss.netty.channel.group.ChannelGroup;
28import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070029import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080030import org.onlab.packet.Ip4Address;
31import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080032import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080033import org.onlab.packet.IpPrefix;
Jonathan Hart41349e92015-02-09 14:14:02 -080034import org.onosproject.routingapi.BgpService;
35import org.onosproject.routingapi.RouteListener;
Jonathan Hartab63aac2014-10-16 08:52:55 -070036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080039import java.net.InetAddress;
40import java.net.InetSocketAddress;
41import java.net.SocketAddress;
42import java.util.Collection;
43import java.util.concurrent.ConcurrentHashMap;
44import java.util.concurrent.ConcurrentMap;
45
46import static com.google.common.base.Preconditions.checkNotNull;
47import static java.util.concurrent.Executors.newCachedThreadPool;
48import static org.onlab.util.Tools.namedThreads;
49
Jonathan Hartab63aac2014-10-16 08:52:55 -070050/**
51 * BGP Session Manager class.
52 */
Jonathan Hart41349e92015-02-09 14:14:02 -080053@Component(immediate = true)
54@Service
55public class BgpSessionManager implements BgpInfoService, BgpService {
Jonathan Hartab63aac2014-10-16 08:52:55 -070056 private static final Logger log =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080057 LoggerFactory.getLogger(BgpSessionManager.class);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080058
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080059 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070060 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080061 private ServerBootstrap serverBootstrap;
62 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070063 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080064 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080065 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070066
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080067 private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
68 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080069 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080070 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080071 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070072
Jonathan Hart41349e92015-02-09 14:14:02 -080073 private RouteListener routeListener;
Jonathan Hartab63aac2014-10-16 08:52:55 -070074
75 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080076 * Checks whether the BGP Session Manager is shutdown.
77 *
78 * @return true if the BGP Session Manager is shutdown, otherwise false
79 */
80 boolean isShutdown() {
81 return this.isShutdown;
82 }
83
84 /**
85 * Gets the route listener.
86 *
87 * @return the route listener to use
88 */
89 RouteListener getRouteListener() {
90 return routeListener;
91 }
92
93 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -070094 * Gets the BGP sessions.
95 *
96 * @return the BGP sessions
97 */
98 public Collection<BgpSession> getBgpSessions() {
99 return bgpSessions.values();
100 }
101
102 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800103 * Gets the selected IPv4 BGP routes among all BGP sessions.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700104 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800105 * @return the selected IPv4 BGP routes among all BGP sessions
Jonathan Hartab63aac2014-10-16 08:52:55 -0700106 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800107 public Collection<BgpRouteEntry> getBgpRoutes4() {
108 return bgpRoutes4.values();
109 }
110
111 /**
112 * Gets the selected IPv6 BGP routes among all BGP sessions.
113 *
114 * @return the selected IPv6 BGP routes among all BGP sessions
115 */
116 public Collection<BgpRouteEntry> getBgpRoutes6() {
117 return bgpRoutes6.values();
118 }
119
120 /**
121 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
122 *
123 * @param prefix the prefix to use
124 * @return the BGP route if found, otherwise null
125 */
126 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
127 if (prefix.version() == Ip4Address.VERSION) {
128 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
129 }
130 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
131 }
132
133 /**
134 * Adds a BGP route. The route can be either IPv4 or IPv6.
135 *
136 * @param bgpRouteEntry the BGP route entry to use
137 */
138 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
139 if (bgpRouteEntry.version() == Ip4Address.VERSION) {
140 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
141 bgpRouteEntry);
142 } else {
143 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
144 bgpRouteEntry);
145 }
146 }
147
148 /**
149 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
150 *
151 * @param prefix the prefix to use
152 * @return true if the route was found and removed, otherwise false
153 */
154 boolean removeBgpRoute(IpPrefix prefix) {
155 if (prefix.version() == Ip4Address.VERSION) {
156 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
157 }
158 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700159 }
160
161 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800162 * Adds the channel for a BGP session.
163 *
164 * @param channel the channel to add
165 */
166 void addSessionChannel(Channel channel) {
167 allChannels.add(channel);
168 }
169
170 /**
171 * Removes the channel for a BGP session.
172 *
173 * @param channel the channel to remove
174 */
175 void removeSessionChannel(Channel channel) {
176 allChannels.remove(channel);
177 }
178
179 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700180 * Processes the connection from a BGP peer.
181 *
182 * @param bgpSession the BGP session for the peer
183 * @return true if the connection can be established, otherwise false
184 */
185 boolean peerConnected(BgpSession bgpSession) {
186
187 // Test whether there is already a session from the same remote
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800188 if (bgpSessions.get(bgpSession.remoteInfo().address()) != null) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700189 return false; // Duplicate BGP session
190 }
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800191 bgpSessions.put(bgpSession.remoteInfo().address(), bgpSession);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700192
193 //
194 // If the first connection, set my BGP ID to the local address
195 // of the socket.
196 //
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800197 if (bgpSession.localInfo().address() instanceof InetSocketAddress) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700198 InetAddress inetAddr =
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800199 ((InetSocketAddress) bgpSession.localInfo().address()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800200 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700201 updateMyBgpId(ip4Address);
202 }
203 return true;
204 }
205
206 /**
207 * Processes the disconnection from a BGP peer.
208 *
209 * @param bgpSession the BGP session for the peer
210 */
211 void peerDisconnected(BgpSession bgpSession) {
Pavlin Radoslavov8a36ce32015-01-28 12:26:57 -0800212 bgpSessions.remove(bgpSession.remoteInfo().address());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700213 }
214
215 /**
216 * Conditionally updates the local BGP ID if it wasn't set already.
217 * <p/>
218 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
219 *
220 * @param ip4Address the IPv4 address to use as BGP ID
221 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800222 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700223 if (myBgpId == null) {
224 myBgpId = ip4Address;
225 log.debug("BGP: My BGP ID is {}", myBgpId);
226 }
227 }
228
229 /**
230 * Gets the local BGP Identifier as an IPv4 address.
231 *
232 * @return the local BGP Identifier as an IPv4 address
233 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800234 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700235 return myBgpId;
236 }
237
238 /**
239 * Gets the BGP Route Selector.
240 *
241 * @return the BGP Route Selector
242 */
243 BgpRouteSelector getBgpRouteSelector() {
244 return bgpRouteSelector;
245 }
246
Jonathan Hart41349e92015-02-09 14:14:02 -0800247 @Override
248 public void start(RouteListener routeListener, int listenPortNumber) {
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800249 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800250 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700251
Jonathan Hart41349e92015-02-09 14:14:02 -0800252 this.routeListener = checkNotNull(routeListener);
253
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800254 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800255 newCachedThreadPool(namedThreads("onos-bgp-sm-boss-%d")),
256 newCachedThreadPool(namedThreads("onos-bgp-sm-worker-%d")));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700257 ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800258 @Override
259 public ChannelPipeline getPipeline() throws Exception {
260 // Allocate a new session per connection
261 BgpSession bgpSessionHandler =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700262 new BgpSession(BgpSessionManager.this);
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800263 BgpFrameDecoder bgpFrameDecoder =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700264 new BgpFrameDecoder(bgpSessionHandler);
265
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800266 // Setup the processing pipeline
267 ChannelPipeline pipeline = Channels.pipeline();
268 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
269 pipeline.addLast("BgpSession", bgpSessionHandler);
270 return pipeline;
271 }
272 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700273 InetSocketAddress listenAddress =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800274 new InetSocketAddress(listenPortNumber);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700275
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800276 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700277 // serverBootstrap.setOptions("reuseAddr", true);
278 serverBootstrap.setOption("child.keepAlive", true);
279 serverBootstrap.setOption("child.tcpNoDelay", true);
280 serverBootstrap.setPipelineFactory(pipelineFactory);
281 try {
282 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800283 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700284 } catch (ChannelException e) {
285 log.debug("Exception binding to BGP port {}: ",
286 listenAddress.getPort(), e);
287 }
288 }
289
Jonathan Hart41349e92015-02-09 14:14:02 -0800290 @Override
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800291 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800292 isShutdown = true;
293 allChannels.close().awaitUninterruptibly();
294 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700295 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700296}