blob: 557e9b6693db551acf7bb82655c629d22a4cf44f [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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.sdnip.bgp;
Jonathan Hartab63aac2014-10-16 08:52:55 -070017
Jonathan Hartab63aac2014-10-16 08:52:55 -070018import org.jboss.netty.bootstrap.ServerBootstrap;
19import org.jboss.netty.channel.Channel;
20import org.jboss.netty.channel.ChannelException;
21import org.jboss.netty.channel.ChannelFactory;
22import org.jboss.netty.channel.ChannelPipeline;
23import org.jboss.netty.channel.ChannelPipelineFactory;
24import org.jboss.netty.channel.Channels;
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080025import org.jboss.netty.channel.group.ChannelGroup;
26import org.jboss.netty.channel.group.DefaultChannelGroup;
Jonathan Hartab63aac2014-10-16 08:52:55 -070027import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080028import org.onlab.packet.Ip4Address;
29import org.onlab.packet.Ip4Prefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080030import org.onlab.packet.Ip6Prefix;
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080031import org.onlab.packet.IpPrefix;
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080032import org.onosproject.sdnip.RouteListener;
Jonathan Hartab63aac2014-10-16 08:52:55 -070033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080036import java.net.InetAddress;
37import java.net.InetSocketAddress;
38import java.net.SocketAddress;
39import java.util.Collection;
40import java.util.concurrent.ConcurrentHashMap;
41import java.util.concurrent.ConcurrentMap;
42
43import static com.google.common.base.Preconditions.checkNotNull;
44import static java.util.concurrent.Executors.newCachedThreadPool;
45import static org.onlab.util.Tools.namedThreads;
46
Jonathan Hartab63aac2014-10-16 08:52:55 -070047/**
48 * BGP Session Manager class.
49 */
50public class BgpSessionManager {
51 private static final Logger log =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080052 LoggerFactory.getLogger(BgpSessionManager.class);
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080053
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080054 boolean isShutdown = true;
Jonathan Hartab63aac2014-10-16 08:52:55 -070055 private Channel serverChannel; // Listener for incoming BGP connections
Pavlin Radoslavov7e190942014-11-13 18:50:59 -080056 private ServerBootstrap serverBootstrap;
57 private ChannelGroup allChannels = new DefaultChannelGroup();
Jonathan Hartab63aac2014-10-16 08:52:55 -070058 private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080059 new ConcurrentHashMap<>();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -080060 private Ip4Address myBgpId; // Same BGP ID for all peers
Jonathan Hartab63aac2014-10-16 08:52:55 -070061
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080062 private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
63 private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080064 new ConcurrentHashMap<>();
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080065 private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -080066 new ConcurrentHashMap<>();
Jonathan Hartab63aac2014-10-16 08:52:55 -070067
68 private final RouteListener routeListener;
69
70 /**
71 * Constructor for given route listener.
72 *
73 * @param routeListener the route listener to use
74 */
75 public BgpSessionManager(RouteListener routeListener) {
76 this.routeListener = checkNotNull(routeListener);
77 }
78
79 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -080080 * Checks whether the BGP Session Manager is shutdown.
81 *
82 * @return true if the BGP Session Manager is shutdown, otherwise false
83 */
84 boolean isShutdown() {
85 return this.isShutdown;
86 }
87
88 /**
89 * Gets the route listener.
90 *
91 * @return the route listener to use
92 */
93 RouteListener getRouteListener() {
94 return routeListener;
95 }
96
97 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -070098 * Gets the BGP sessions.
99 *
100 * @return the BGP sessions
101 */
102 public Collection<BgpSession> getBgpSessions() {
103 return bgpSessions.values();
104 }
105
106 /**
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800107 * Gets the selected IPv4 BGP routes among all BGP sessions.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700108 *
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800109 * @return the selected IPv4 BGP routes among all BGP sessions
Jonathan Hartab63aac2014-10-16 08:52:55 -0700110 */
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800111 public Collection<BgpRouteEntry> getBgpRoutes4() {
112 return bgpRoutes4.values();
113 }
114
115 /**
116 * Gets the selected IPv6 BGP routes among all BGP sessions.
117 *
118 * @return the selected IPv6 BGP routes among all BGP sessions
119 */
120 public Collection<BgpRouteEntry> getBgpRoutes6() {
121 return bgpRoutes6.values();
122 }
123
124 /**
125 * Finds a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
126 *
127 * @param prefix the prefix to use
128 * @return the BGP route if found, otherwise null
129 */
130 BgpRouteEntry findBgpRoute(IpPrefix prefix) {
131 if (prefix.version() == Ip4Address.VERSION) {
132 return bgpRoutes4.get(prefix.getIp4Prefix()); // IPv4
133 }
134 return bgpRoutes6.get(prefix.getIp6Prefix()); // IPv6
135 }
136
137 /**
138 * Adds a BGP route. The route can be either IPv4 or IPv6.
139 *
140 * @param bgpRouteEntry the BGP route entry to use
141 */
142 void addBgpRoute(BgpRouteEntry bgpRouteEntry) {
143 if (bgpRouteEntry.version() == Ip4Address.VERSION) {
144 bgpRoutes4.put(bgpRouteEntry.prefix().getIp4Prefix(), // IPv4
145 bgpRouteEntry);
146 } else {
147 bgpRoutes6.put(bgpRouteEntry.prefix().getIp6Prefix(), // IPv6
148 bgpRouteEntry);
149 }
150 }
151
152 /**
153 * Removes a BGP route for a prefix. The prefix can be either IPv4 or IPv6.
154 *
155 * @param prefix the prefix to use
156 * @return true if the route was found and removed, otherwise false
157 */
158 boolean removeBgpRoute(IpPrefix prefix) {
159 if (prefix.version() == Ip4Address.VERSION) {
160 return (bgpRoutes4.remove(prefix.getIp4Prefix()) != null); // IPv4
161 }
162 return (bgpRoutes6.remove(prefix.getIp6Prefix()) != null); // IPv6
Jonathan Hartab63aac2014-10-16 08:52:55 -0700163 }
164
165 /**
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800166 * Adds the channel for a BGP session.
167 *
168 * @param channel the channel to add
169 */
170 void addSessionChannel(Channel channel) {
171 allChannels.add(channel);
172 }
173
174 /**
175 * Removes the channel for a BGP session.
176 *
177 * @param channel the channel to remove
178 */
179 void removeSessionChannel(Channel channel) {
180 allChannels.remove(channel);
181 }
182
183 /**
Jonathan Hartab63aac2014-10-16 08:52:55 -0700184 * Processes the connection from a BGP peer.
185 *
186 * @param bgpSession the BGP session for the peer
187 * @return true if the connection can be established, otherwise false
188 */
189 boolean peerConnected(BgpSession bgpSession) {
190
191 // Test whether there is already a session from the same remote
192 if (bgpSessions.get(bgpSession.getRemoteAddress()) != null) {
193 return false; // Duplicate BGP session
194 }
195 bgpSessions.put(bgpSession.getRemoteAddress(), bgpSession);
196
197 //
198 // If the first connection, set my BGP ID to the local address
199 // of the socket.
200 //
201 if (bgpSession.getLocalAddress() instanceof InetSocketAddress) {
202 InetAddress inetAddr =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800203 ((InetSocketAddress) bgpSession.getLocalAddress()).getAddress();
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800204 Ip4Address ip4Address = Ip4Address.valueOf(inetAddr.getAddress());
Jonathan Hartab63aac2014-10-16 08:52:55 -0700205 updateMyBgpId(ip4Address);
206 }
207 return true;
208 }
209
210 /**
211 * Processes the disconnection from a BGP peer.
212 *
213 * @param bgpSession the BGP session for the peer
214 */
215 void peerDisconnected(BgpSession bgpSession) {
216 bgpSessions.remove(bgpSession.getRemoteAddress());
217 }
218
219 /**
220 * Conditionally updates the local BGP ID if it wasn't set already.
221 * <p/>
222 * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
223 *
224 * @param ip4Address the IPv4 address to use as BGP ID
225 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800226 private synchronized void updateMyBgpId(Ip4Address ip4Address) {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700227 if (myBgpId == null) {
228 myBgpId = ip4Address;
229 log.debug("BGP: My BGP ID is {}", myBgpId);
230 }
231 }
232
233 /**
234 * Gets the local BGP Identifier as an IPv4 address.
235 *
236 * @return the local BGP Identifier as an IPv4 address
237 */
Pavlin Radoslavov6b570732014-11-06 13:16:45 -0800238 Ip4Address getMyBgpId() {
Jonathan Hartab63aac2014-10-16 08:52:55 -0700239 return myBgpId;
240 }
241
242 /**
243 * Gets the BGP Route Selector.
244 *
245 * @return the BGP Route Selector
246 */
247 BgpRouteSelector getBgpRouteSelector() {
248 return bgpRouteSelector;
249 }
250
251 /**
252 * Starts up BGP Session Manager operation.
253 *
254 * @param listenPortNumber the port number to listen on. By default
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800255 * it should be BgpConstants.BGP_PORT (179)
Jonathan Hartab63aac2014-10-16 08:52:55 -0700256 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800257 public void start(int listenPortNumber) {
258 log.debug("BGP Session Manager start.");
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800259 isShutdown = false;
Jonathan Hartab63aac2014-10-16 08:52:55 -0700260
Pavlin Radoslavov3a0a52e2015-01-06 17:41:37 -0800261 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800262 newCachedThreadPool(namedThreads("onos-bgp-sm-boss-%d")),
263 newCachedThreadPool(namedThreads("onos-bgp-sm-worker-%d")));
Jonathan Hartab63aac2014-10-16 08:52:55 -0700264 ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800265 @Override
266 public ChannelPipeline getPipeline() throws Exception {
267 // Allocate a new session per connection
268 BgpSession bgpSessionHandler =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700269 new BgpSession(BgpSessionManager.this);
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800270 BgpFrameDecoder bgpFrameDecoder =
Jonathan Hartab63aac2014-10-16 08:52:55 -0700271 new BgpFrameDecoder(bgpSessionHandler);
272
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800273 // Setup the processing pipeline
274 ChannelPipeline pipeline = Channels.pipeline();
275 pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
276 pipeline.addLast("BgpSession", bgpSessionHandler);
277 return pipeline;
278 }
279 };
Jonathan Hartab63aac2014-10-16 08:52:55 -0700280 InetSocketAddress listenAddress =
Thomas Vachuska9ea3e6f2015-01-23 16:34:22 -0800281 new InetSocketAddress(listenPortNumber);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700282
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800283 serverBootstrap = new ServerBootstrap(channelFactory);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700284 // serverBootstrap.setOptions("reuseAddr", true);
285 serverBootstrap.setOption("child.keepAlive", true);
286 serverBootstrap.setOption("child.tcpNoDelay", true);
287 serverBootstrap.setPipelineFactory(pipelineFactory);
288 try {
289 serverChannel = serverBootstrap.bind(listenAddress);
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800290 allChannels.add(serverChannel);
Jonathan Hartab63aac2014-10-16 08:52:55 -0700291 } catch (ChannelException e) {
292 log.debug("Exception binding to BGP port {}: ",
293 listenAddress.getPort(), e);
294 }
295 }
296
297 /**
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800298 * Stops the BGP Session Manager operation.
Jonathan Hartab63aac2014-10-16 08:52:55 -0700299 */
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800300 public void stop() {
Pavlin Radoslavov7e190942014-11-13 18:50:59 -0800301 isShutdown = true;
302 allChannels.close().awaitUninterruptibly();
303 serverBootstrap.releaseExternalResources();
Jonathan Hartab63aac2014-10-16 08:52:55 -0700304 }
Jonathan Hartab63aac2014-10-16 08:52:55 -0700305}