blob: 582835ca9672a2a0a5322f6c204ba0a373ebaa46 [file] [log] [blame]
Jonathan Hart3930f632015-10-19 12:12:51 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Jonathan Hart3930f632015-10-19 12:12:51 -07003 *
4 * 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
7 *
8 * 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.
15 */
16package org.onosproject.routing.fpm;
17
Jonathan Hart6b045582016-02-03 10:00:08 -080018import com.google.common.collect.ImmutableMap;
Jonathan Hart3930f632015-10-19 12:12:51 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart3930f632015-10-19 12:12:51 -070026import org.apache.felix.scr.annotations.Service;
27import org.jboss.netty.bootstrap.ServerBootstrap;
28import org.jboss.netty.channel.Channel;
29import org.jboss.netty.channel.ChannelException;
30import org.jboss.netty.channel.ChannelFactory;
31import org.jboss.netty.channel.ChannelPipeline;
32import org.jboss.netty.channel.ChannelPipelineFactory;
33import org.jboss.netty.channel.Channels;
34import org.jboss.netty.channel.group.ChannelGroup;
35import org.jboss.netty.channel.group.DefaultChannelGroup;
36import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
37import org.onlab.packet.IpAddress;
38import org.onlab.packet.IpPrefix;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080039import org.onlab.util.Tools;
40import org.onosproject.cfg.ComponentConfigService;
Jonathan Hart3930f632015-10-19 12:12:51 -070041import org.onosproject.routing.RouteEntry;
42import org.onosproject.routing.RouteListener;
43import org.onosproject.routing.RouteSourceService;
44import org.onosproject.routing.RouteUpdate;
45import org.onosproject.routing.fpm.protocol.FpmHeader;
46import org.onosproject.routing.fpm.protocol.Netlink;
47import org.onosproject.routing.fpm.protocol.RouteAttribute;
48import org.onosproject.routing.fpm.protocol.RouteAttributeDst;
49import org.onosproject.routing.fpm.protocol.RouteAttributeGateway;
50import org.onosproject.routing.fpm.protocol.RtNetlink;
Jonathan Hart916bf892016-01-27 16:42:55 -080051import org.onosproject.routing.fpm.protocol.RtProtocol;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080052import org.osgi.service.component.ComponentContext;
Jonathan Hart3930f632015-10-19 12:12:51 -070053import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.net.InetSocketAddress;
Jonathan Hart6b045582016-02-03 10:00:08 -080057import java.net.SocketAddress;
Jonathan Hart3930f632015-10-19 12:12:51 -070058import java.util.Collections;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080059import java.util.Dictionary;
60import java.util.List;
Jonathan Hart3930f632015-10-19 12:12:51 -070061import java.util.Map;
62import java.util.concurrent.ConcurrentHashMap;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080063import java.util.stream.Collectors;
Jonathan Hart3930f632015-10-19 12:12:51 -070064
65import static java.util.concurrent.Executors.newCachedThreadPool;
66import static org.onlab.util.Tools.groupedThreads;
67
68/**
69 * Forwarding Plane Manager (FPM) route source.
70 */
71@Service
72@Component(immediate = true, enabled = false)
Jonathan Hart6b045582016-02-03 10:00:08 -080073public class FpmManager implements RouteSourceService, FpmInfoService {
Jonathan Hart3930f632015-10-19 12:12:51 -070074 private final Logger log = LoggerFactory.getLogger(getClass());
75
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080076 private static final int FPM_PORT = 2620;
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected ComponentConfigService componentConfigService;
80
Jonathan Hart3930f632015-10-19 12:12:51 -070081 private ServerBootstrap serverBootstrap;
82 private Channel serverChannel;
83 private ChannelGroup allChannels = new DefaultChannelGroup();
84
Jonathan Hart6b045582016-02-03 10:00:08 -080085 private Map<SocketAddress, Long> peers = new ConcurrentHashMap<>();
86
Jonathan Hart3930f632015-10-19 12:12:51 -070087 private Map<IpPrefix, RouteEntry> fpmRoutes = new ConcurrentHashMap<>();
88
89 private RouteListener routeListener;
90
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080091 @Property(name = "clearRoutes", boolValue = true,
92 label = "Whether to clear routes when the FPM connection goes down")
93 private boolean clearRoutes = true;
Jonathan Hart3930f632015-10-19 12:12:51 -070094
95 @Activate
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080096 protected void activate(ComponentContext context) {
97 componentConfigService.registerProperties(getClass());
98 modified(context);
Jonathan Hart3930f632015-10-19 12:12:51 -070099 log.info("Started");
100 }
101
102 @Deactivate
103 protected void deactivate() {
104 stopServer();
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800105 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart3930f632015-10-19 12:12:51 -0700106 log.info("Stopped");
107 }
108
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800109 @Modified
110 protected void modified(ComponentContext context) {
111 Dictionary<?, ?> properties = context.getProperties();
112 if (properties == null) {
113 return;
114 }
115 String strClearRoutes = Tools.get(properties, "clearRoutes");
116 clearRoutes = Boolean.parseBoolean(strClearRoutes);
117
118 log.info("clearRoutes set to {}", clearRoutes);
119 }
120
Jonathan Hart3930f632015-10-19 12:12:51 -0700121 private void startServer() {
122 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
123 newCachedThreadPool(groupedThreads("onos/fpm", "sm-boss-%d")),
124 newCachedThreadPool(groupedThreads("onos/fpm", "sm-worker-%d")));
125 ChannelPipelineFactory pipelineFactory = () -> {
126 // Allocate a new session per connection
127 FpmSessionHandler fpmSessionHandler =
128 new FpmSessionHandler(new InternalFpmListener());
129 FpmFrameDecoder fpmFrameDecoder =
130 new FpmFrameDecoder();
131
132 // Setup the processing pipeline
133 ChannelPipeline pipeline = Channels.pipeline();
134 pipeline.addLast("FpmFrameDecoder", fpmFrameDecoder);
135 pipeline.addLast("FpmSession", fpmSessionHandler);
136 return pipeline;
137 };
138
139 InetSocketAddress listenAddress = new InetSocketAddress(FPM_PORT);
140
141 serverBootstrap = new ServerBootstrap(channelFactory);
142 serverBootstrap.setOption("child.reuseAddr", true);
143 serverBootstrap.setOption("child.keepAlive", true);
144 serverBootstrap.setOption("child.tcpNoDelay", true);
145 serverBootstrap.setPipelineFactory(pipelineFactory);
146 try {
147 serverChannel = serverBootstrap.bind(listenAddress);
148 allChannels.add(serverChannel);
149 } catch (ChannelException e) {
150 log.debug("Exception binding to FPM port {}: ",
151 listenAddress.getPort(), e);
152 stopServer();
153 }
154 }
155
156 private void stopServer() {
157 allChannels.close().awaitUninterruptibly();
158 allChannels.clear();
159 if (serverBootstrap != null) {
160 serverBootstrap.releaseExternalResources();
161 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800162
163 if (clearRoutes) {
164 clearRoutes();
165 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700166 }
167
168 @Override
169 public void start(RouteListener routeListener) {
170 this.routeListener = routeListener;
171
172 startServer();
173 }
174
175 @Override
176 public void stop() {
177 fpmRoutes.clear();
178 stopServer();
179 }
180
181 private void fpmMessage(FpmHeader fpmMessage) {
182 Netlink netlink = fpmMessage.netlink();
183 RtNetlink rtNetlink = netlink.rtNetlink();
184
185 if (log.isTraceEnabled()) {
186 log.trace("Received FPM message: {}", fpmMessage);
187 }
188
Jonathan Hartb3dad102016-03-01 17:42:26 -0800189 if (!(rtNetlink.protocol() == RtProtocol.ZEBRA ||
190 rtNetlink.protocol() == RtProtocol.UNSPEC)) {
Jonathan Hart916bf892016-01-27 16:42:55 -0800191 log.trace("Ignoring non-zebra route");
192 return;
193 }
194
Jonathan Hart3930f632015-10-19 12:12:51 -0700195 IpAddress dstAddress = null;
196 IpAddress gateway = null;
197
198 for (RouteAttribute attribute : rtNetlink.attributes()) {
199 if (attribute.type() == RouteAttribute.RTA_DST) {
200 RouteAttributeDst raDst = (RouteAttributeDst) attribute;
201 dstAddress = raDst.dstAddress();
202 } else if (attribute.type() == RouteAttribute.RTA_GATEWAY) {
203 RouteAttributeGateway raGateway = (RouteAttributeGateway) attribute;
204 gateway = raGateway.gateway();
205 }
206 }
207
208 if (dstAddress == null) {
209 log.error("Dst address missing!");
210 return;
211 }
212
213 IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
214
215 RouteUpdate routeUpdate = null;
216 RouteEntry entry;
217 switch (netlink.type()) {
218 case RTM_NEWROUTE:
219 if (gateway == null) {
220 // We ignore interface routes with no gateway for now.
221 return;
222 }
223 entry = new RouteEntry(prefix, gateway);
224
225 fpmRoutes.put(entry.prefix(), entry);
226
227 routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE, entry);
228 break;
229 case RTM_DELROUTE:
230 RouteEntry existing = fpmRoutes.remove(prefix);
231 if (existing == null) {
232 log.warn("Got delete for non-existent prefix");
233 return;
234 }
235
236 entry = new RouteEntry(prefix, existing.nextHop());
237
238 routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE, entry);
239 break;
240 case RTM_GETROUTE:
241 default:
242 break;
243 }
244
245 if (routeUpdate == null) {
246 log.warn("Unsupported FPM message: {}", fpmMessage);
247 return;
248 }
249
250 routeListener.update(Collections.singletonList(routeUpdate));
251 }
252
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800253
254 private void clearRoutes() {
255 log.info("Clearing all routes");
256 List<RouteUpdate> routeUpdates = fpmRoutes.values().stream()
257 .map(routeEntry -> new RouteUpdate(RouteUpdate.Type.DELETE, routeEntry))
258 .collect(Collectors.toList());
259 routeListener.update(routeUpdates);
260 }
261
Jonathan Hart6b045582016-02-03 10:00:08 -0800262 @Override
263 public Map<SocketAddress, Long> peers() {
264 return ImmutableMap.copyOf(peers);
265 }
266
267 private class InternalFpmListener implements FpmListener {
Jonathan Hart3930f632015-10-19 12:12:51 -0700268 @Override
269 public void fpmMessage(FpmHeader fpmMessage) {
270 FpmManager.this.fpmMessage(fpmMessage);
271 }
Jonathan Hart6b045582016-02-03 10:00:08 -0800272
273 @Override
274 public boolean peerConnected(SocketAddress address) {
275 if (peers.keySet().contains(address)) {
276 return false;
277 }
278
279 peers.put(address, System.currentTimeMillis());
280 return true;
281 }
282
283 @Override
284 public void peerDisconnected(SocketAddress address) {
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800285 log.info("FPM connection to {} went down", address);
286
287 if (clearRoutes) {
288 clearRoutes();
289 }
290
Jonathan Hart6b045582016-02-03 10:00:08 -0800291 peers.remove(address);
292 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700293 }
294
295}