blob: c85cd9cdeb5ad7b44599a6cd4497969849cfb9b4 [file] [log] [blame]
Jonathan Hart3930f632015-10-19 12:12:51 -07001/*
Jonathan Hartf4bd0482017-01-27 15:11:18 -08002 * Copyright 2017-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 */
Jonathan Hartf4bd0482017-01-27 15:11:18 -080016
Jonathan Hart3930f632015-10-19 12:12:51 -070017package org.onosproject.routing.fpm;
18
Jonathan Hart6b045582016-02-03 10:00:08 -080019import com.google.common.collect.ImmutableMap;
Jonathan Hart3930f632015-10-19 12:12:51 -070020import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080023import org.apache.felix.scr.annotations.Modified;
24import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart3930f632015-10-19 12:12:51 -070027import org.apache.felix.scr.annotations.Service;
28import org.jboss.netty.bootstrap.ServerBootstrap;
29import org.jboss.netty.channel.Channel;
30import org.jboss.netty.channel.ChannelException;
31import org.jboss.netty.channel.ChannelFactory;
32import org.jboss.netty.channel.ChannelPipeline;
33import org.jboss.netty.channel.ChannelPipelineFactory;
34import org.jboss.netty.channel.Channels;
35import org.jboss.netty.channel.group.ChannelGroup;
36import org.jboss.netty.channel.group.DefaultChannelGroup;
37import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
38import org.onlab.packet.IpAddress;
39import org.onlab.packet.IpPrefix;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070040import org.onlab.util.KryoNamespace;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080041import org.onlab.util.Tools;
42import org.onosproject.cfg.ComponentConfigService;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070043import org.onosproject.cluster.ClusterService;
44import org.onosproject.cluster.NodeId;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070045import org.onosproject.incubator.net.routing.Route;
46import org.onosproject.incubator.net.routing.RouteAdminService;
Jonathan Hart3930f632015-10-19 12:12:51 -070047import org.onosproject.routing.fpm.protocol.FpmHeader;
48import org.onosproject.routing.fpm.protocol.Netlink;
49import org.onosproject.routing.fpm.protocol.RouteAttribute;
50import org.onosproject.routing.fpm.protocol.RouteAttributeDst;
51import org.onosproject.routing.fpm.protocol.RouteAttributeGateway;
52import org.onosproject.routing.fpm.protocol.RtNetlink;
Jonathan Hart916bf892016-01-27 16:42:55 -080053import org.onosproject.routing.fpm.protocol.RtProtocol;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070054import org.onosproject.store.serializers.KryoNamespaces;
55import org.onosproject.store.service.ConsistentMap;
56import org.onosproject.store.service.Serializer;
57import org.onosproject.store.service.StorageService;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080058import org.osgi.service.component.ComponentContext;
Jonathan Hart3930f632015-10-19 12:12:51 -070059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61
62import java.net.InetSocketAddress;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070063import java.util.Collection;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080064import java.util.Dictionary;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070065import java.util.HashSet;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070066import java.util.LinkedList;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080067import java.util.List;
Jonathan Hart3930f632015-10-19 12:12:51 -070068import java.util.Map;
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070069import java.util.Set;
Jonathan Hart3930f632015-10-19 12:12:51 -070070import java.util.concurrent.ConcurrentHashMap;
71
72import static java.util.concurrent.Executors.newCachedThreadPool;
73import static org.onlab.util.Tools.groupedThreads;
74
75/**
76 * Forwarding Plane Manager (FPM) route source.
77 */
78@Service
Jonathan Hartf4bd0482017-01-27 15:11:18 -080079@Component(immediate = true)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070080public class FpmManager implements FpmInfoService {
Jonathan Hart3930f632015-10-19 12:12:51 -070081 private final Logger log = LoggerFactory.getLogger(getClass());
82
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080083 private static final int FPM_PORT = 2620;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected ComponentConfigService componentConfigService;
87
Jonathan Hart1ad75f22016-04-13 21:24:13 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected RouteAdminService routeService;
90
Jonathan Hartdc7e76c2017-03-27 11:35:34 -070091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected ClusterService clusterService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected StorageService storageService;
96
Jonathan Hart3930f632015-10-19 12:12:51 -070097 private ServerBootstrap serverBootstrap;
98 private Channel serverChannel;
99 private ChannelGroup allChannels = new DefaultChannelGroup();
100
Jonathan Hartdc7e76c2017-03-27 11:35:34 -0700101 private ConsistentMap<FpmPeer, Set<FpmConnectionInfo>> peers;
Jonathan Hart6b045582016-02-03 10:00:08 -0800102
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700103 private Map<FpmPeer, Map<IpPrefix, Route>> fpmRoutes = new ConcurrentHashMap<>();
Jonathan Hart3930f632015-10-19 12:12:51 -0700104
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800105 @Property(name = "clearRoutes", boolValue = true,
106 label = "Whether to clear routes when the FPM connection goes down")
107 private boolean clearRoutes = true;
Jonathan Hart3930f632015-10-19 12:12:51 -0700108
109 @Activate
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800110 protected void activate(ComponentContext context) {
Charles Chane7926852017-02-02 11:41:19 -0800111 componentConfigService.preSetProperty(
112 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
113 "distributed", "true");
114
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800115 componentConfigService.registerProperties(getClass());
Jonathan Hartdc7e76c2017-03-27 11:35:34 -0700116
117 KryoNamespace serializer = KryoNamespace.newBuilder()
118 .register(KryoNamespaces.API)
119 .register(FpmPeer.class)
120 .register(FpmConnectionInfo.class)
121 .build();
122 peers = storageService.<FpmPeer, Set<FpmConnectionInfo>>consistentMapBuilder()
123 .withName("fpm-connections")
124 .withSerializer(Serializer.using(serializer))
125 .build();
126
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800127 modified(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700128 startServer();
Jonathan Hart3930f632015-10-19 12:12:51 -0700129 log.info("Started");
130 }
131
132 @Deactivate
133 protected void deactivate() {
Charles Chane7926852017-02-02 11:41:19 -0800134 componentConfigService.preSetProperty(
135 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
136 "distributed", "false");
137
Jonathan Hart3930f632015-10-19 12:12:51 -0700138 stopServer();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700139 fpmRoutes.clear();
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800140 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart3930f632015-10-19 12:12:51 -0700141 log.info("Stopped");
142 }
143
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800144 @Modified
145 protected void modified(ComponentContext context) {
146 Dictionary<?, ?> properties = context.getProperties();
147 if (properties == null) {
148 return;
149 }
150 String strClearRoutes = Tools.get(properties, "clearRoutes");
151 clearRoutes = Boolean.parseBoolean(strClearRoutes);
152
153 log.info("clearRoutes set to {}", clearRoutes);
154 }
155
Jonathan Hart3930f632015-10-19 12:12:51 -0700156 private void startServer() {
157 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700158 newCachedThreadPool(groupedThreads("onos/fpm", "sm-boss-%d", log)),
159 newCachedThreadPool(groupedThreads("onos/fpm", "sm-worker-%d", log)));
Jonathan Hart3930f632015-10-19 12:12:51 -0700160 ChannelPipelineFactory pipelineFactory = () -> {
161 // Allocate a new session per connection
162 FpmSessionHandler fpmSessionHandler =
163 new FpmSessionHandler(new InternalFpmListener());
164 FpmFrameDecoder fpmFrameDecoder =
165 new FpmFrameDecoder();
166
167 // Setup the processing pipeline
168 ChannelPipeline pipeline = Channels.pipeline();
169 pipeline.addLast("FpmFrameDecoder", fpmFrameDecoder);
170 pipeline.addLast("FpmSession", fpmSessionHandler);
171 return pipeline;
172 };
173
174 InetSocketAddress listenAddress = new InetSocketAddress(FPM_PORT);
175
176 serverBootstrap = new ServerBootstrap(channelFactory);
177 serverBootstrap.setOption("child.reuseAddr", true);
178 serverBootstrap.setOption("child.keepAlive", true);
179 serverBootstrap.setOption("child.tcpNoDelay", true);
180 serverBootstrap.setPipelineFactory(pipelineFactory);
181 try {
182 serverChannel = serverBootstrap.bind(listenAddress);
183 allChannels.add(serverChannel);
184 } catch (ChannelException e) {
185 log.debug("Exception binding to FPM port {}: ",
186 listenAddress.getPort(), e);
187 stopServer();
188 }
189 }
190
191 private void stopServer() {
192 allChannels.close().awaitUninterruptibly();
193 allChannels.clear();
194 if (serverBootstrap != null) {
195 serverBootstrap.releaseExternalResources();
196 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800197
198 if (clearRoutes) {
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700199 peers.keySet().forEach(this::clearRoutes);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800200 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700201 }
202
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700203 private void fpmMessage(FpmPeer peer, FpmHeader fpmMessage) {
Jonathan Hart3930f632015-10-19 12:12:51 -0700204 Netlink netlink = fpmMessage.netlink();
205 RtNetlink rtNetlink = netlink.rtNetlink();
206
207 if (log.isTraceEnabled()) {
208 log.trace("Received FPM message: {}", fpmMessage);
209 }
210
Jonathan Hartb3dad102016-03-01 17:42:26 -0800211 if (!(rtNetlink.protocol() == RtProtocol.ZEBRA ||
212 rtNetlink.protocol() == RtProtocol.UNSPEC)) {
Jonathan Hart916bf892016-01-27 16:42:55 -0800213 log.trace("Ignoring non-zebra route");
214 return;
215 }
216
Jonathan Hart3930f632015-10-19 12:12:51 -0700217 IpAddress dstAddress = null;
218 IpAddress gateway = null;
219
220 for (RouteAttribute attribute : rtNetlink.attributes()) {
221 if (attribute.type() == RouteAttribute.RTA_DST) {
222 RouteAttributeDst raDst = (RouteAttributeDst) attribute;
223 dstAddress = raDst.dstAddress();
224 } else if (attribute.type() == RouteAttribute.RTA_GATEWAY) {
225 RouteAttributeGateway raGateway = (RouteAttributeGateway) attribute;
226 gateway = raGateway.gateway();
227 }
228 }
229
230 if (dstAddress == null) {
231 log.error("Dst address missing!");
232 return;
233 }
234
235 IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
236
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700237 List<Route> updates = new LinkedList<>();
238 List<Route> withdraws = new LinkedList<>();
239
240 Route route;
Jonathan Hart3930f632015-10-19 12:12:51 -0700241 switch (netlink.type()) {
242 case RTM_NEWROUTE:
243 if (gateway == null) {
244 // We ignore interface routes with no gateway for now.
245 return;
246 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700247 route = new Route(Route.Source.FPM, prefix, gateway);
Jonathan Hart3930f632015-10-19 12:12:51 -0700248
Jonathan Hart3930f632015-10-19 12:12:51 -0700249
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700250 Route oldRoute = fpmRoutes.get(peer).put(prefix, route);
251
252 if (oldRoute != null) {
253 log.trace("Swapping {} with {}", oldRoute, route);
254 withdraws.add(oldRoute);
255 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700256 updates.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700257 break;
258 case RTM_DELROUTE:
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700259 Route existing = fpmRoutes.get(peer).remove(prefix);
Jonathan Hart3930f632015-10-19 12:12:51 -0700260 if (existing == null) {
261 log.warn("Got delete for non-existent prefix");
262 return;
263 }
264
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700265 route = new Route(Route.Source.FPM, prefix, existing.nextHop());
Jonathan Hart3930f632015-10-19 12:12:51 -0700266
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700267 withdraws.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700268 break;
269 case RTM_GETROUTE:
270 default:
271 break;
272 }
273
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700274 routeService.withdraw(withdraws);
275 routeService.update(updates);
Jonathan Hart3930f632015-10-19 12:12:51 -0700276 }
277
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800278
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700279 private void clearRoutes(FpmPeer peer) {
280 log.info("Clearing all routes for peer {}", peer);
281 Map<IpPrefix, Route> routes = fpmRoutes.remove(peer);
282 if (routes != null) {
283 routeService.withdraw(routes.values());
284 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800285 }
286
Jonathan Hart6b045582016-02-03 10:00:08 -0800287 @Override
Jonathan Hartdc7e76c2017-03-27 11:35:34 -0700288 public Map<FpmPeer, Collection<FpmConnectionInfo>> peers() {
289 return ImmutableMap.<FpmPeer, Collection<FpmConnectionInfo>>builder()
290 .putAll(peers.asJavaMap())
291 .build();
Jonathan Hart6b045582016-02-03 10:00:08 -0800292 }
293
294 private class InternalFpmListener implements FpmListener {
Jonathan Hart3930f632015-10-19 12:12:51 -0700295 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700296 public void fpmMessage(FpmPeer peer, FpmHeader fpmMessage) {
297 FpmManager.this.fpmMessage(peer, fpmMessage);
Jonathan Hart3930f632015-10-19 12:12:51 -0700298 }
Jonathan Hart6b045582016-02-03 10:00:08 -0800299
300 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700301 public boolean peerConnected(FpmPeer peer) {
302 if (peers.keySet().contains(peer)) {
Jonathan Hart6b045582016-02-03 10:00:08 -0800303 return false;
304 }
305
Jonathan Hartdc7e76c2017-03-27 11:35:34 -0700306 NodeId localNode = clusterService.getLocalNode().id();
307 peers.compute(peer, (p, infos) -> {
308 if (infos == null) {
309 infos = new HashSet<>();
310 }
311
312 infos.add(new FpmConnectionInfo(localNode, peer, System.currentTimeMillis()));
313 return infos;
314 });
315
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700316 fpmRoutes.computeIfAbsent(peer, p -> new ConcurrentHashMap<>());
Jonathan Hart6b045582016-02-03 10:00:08 -0800317 return true;
318 }
319
320 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700321 public void peerDisconnected(FpmPeer peer) {
322 log.info("FPM connection to {} went down", peer);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800323
324 if (clearRoutes) {
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700325 clearRoutes(peer);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800326 }
327
Jonathan Hartdc7e76c2017-03-27 11:35:34 -0700328 peers.compute(peer, (p, infos) -> {
329 infos.stream()
330 .filter(i -> i.connectedTo().equals(clusterService.getLocalNode().id()))
331 .findAny()
332 .ifPresent(i -> infos.remove(i));
333
334 if (infos.isEmpty()) {
335 return null;
336 }
337
338 return infos;
339 });
Jonathan Hart6b045582016-02-03 10:00:08 -0800340 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700341 }
342
343}