blob: 9019960e069b95f953ef8adac845a9501e220a0a [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 Hart1af5a7a2016-02-15 08:51:27 -080040import org.onlab.util.Tools;
41import org.onosproject.cfg.ComponentConfigService;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070042import org.onosproject.incubator.net.routing.Route;
43import org.onosproject.incubator.net.routing.RouteAdminService;
Jonathan Hart3930f632015-10-19 12:12:51 -070044import org.onosproject.routing.fpm.protocol.FpmHeader;
45import org.onosproject.routing.fpm.protocol.Netlink;
46import org.onosproject.routing.fpm.protocol.RouteAttribute;
47import org.onosproject.routing.fpm.protocol.RouteAttributeDst;
48import org.onosproject.routing.fpm.protocol.RouteAttributeGateway;
49import org.onosproject.routing.fpm.protocol.RtNetlink;
Jonathan Hart916bf892016-01-27 16:42:55 -080050import org.onosproject.routing.fpm.protocol.RtProtocol;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080051import org.osgi.service.component.ComponentContext;
Jonathan Hart3930f632015-10-19 12:12:51 -070052import org.slf4j.Logger;
53import org.slf4j.LoggerFactory;
54
55import java.net.InetSocketAddress;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080056import java.util.Dictionary;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070057import java.util.LinkedList;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080058import java.util.List;
Jonathan Hart3930f632015-10-19 12:12:51 -070059import java.util.Map;
60import java.util.concurrent.ConcurrentHashMap;
61
62import static java.util.concurrent.Executors.newCachedThreadPool;
63import static org.onlab.util.Tools.groupedThreads;
64
65/**
66 * Forwarding Plane Manager (FPM) route source.
67 */
68@Service
Jonathan Hartf4bd0482017-01-27 15:11:18 -080069@Component(immediate = true)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070070public class FpmManager implements FpmInfoService {
Jonathan Hart3930f632015-10-19 12:12:51 -070071 private final Logger log = LoggerFactory.getLogger(getClass());
72
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080073 private static final int FPM_PORT = 2620;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected ComponentConfigService componentConfigService;
77
Jonathan Hart1ad75f22016-04-13 21:24:13 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected RouteAdminService routeService;
80
Jonathan Hart3930f632015-10-19 12:12:51 -070081 private ServerBootstrap serverBootstrap;
82 private Channel serverChannel;
83 private ChannelGroup allChannels = new DefaultChannelGroup();
84
Jonathan Hartb10f1e72017-05-02 16:36:26 -070085 private Map<FpmPeer, Long> peers = new ConcurrentHashMap<>();
Jonathan Hart6b045582016-02-03 10:00:08 -080086
Jonathan Hartb10f1e72017-05-02 16:36:26 -070087 private Map<FpmPeer, Map<IpPrefix, Route>> fpmRoutes = new ConcurrentHashMap<>();
Jonathan Hart3930f632015-10-19 12:12:51 -070088
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080089 @Property(name = "clearRoutes", boolValue = true,
90 label = "Whether to clear routes when the FPM connection goes down")
91 private boolean clearRoutes = true;
Jonathan Hart3930f632015-10-19 12:12:51 -070092
93 @Activate
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080094 protected void activate(ComponentContext context) {
Charles Chane7926852017-02-02 11:41:19 -080095 componentConfigService.preSetProperty(
96 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
97 "distributed", "true");
98
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080099 componentConfigService.registerProperties(getClass());
100 modified(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700101 startServer();
Jonathan Hart3930f632015-10-19 12:12:51 -0700102 log.info("Started");
103 }
104
105 @Deactivate
106 protected void deactivate() {
Charles Chane7926852017-02-02 11:41:19 -0800107 componentConfigService.preSetProperty(
108 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
109 "distributed", "false");
110
Jonathan Hart3930f632015-10-19 12:12:51 -0700111 stopServer();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700112 fpmRoutes.clear();
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800113 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart3930f632015-10-19 12:12:51 -0700114 log.info("Stopped");
115 }
116
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800117 @Modified
118 protected void modified(ComponentContext context) {
119 Dictionary<?, ?> properties = context.getProperties();
120 if (properties == null) {
121 return;
122 }
123 String strClearRoutes = Tools.get(properties, "clearRoutes");
124 clearRoutes = Boolean.parseBoolean(strClearRoutes);
125
126 log.info("clearRoutes set to {}", clearRoutes);
127 }
128
Jonathan Hart3930f632015-10-19 12:12:51 -0700129 private void startServer() {
130 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700131 newCachedThreadPool(groupedThreads("onos/fpm", "sm-boss-%d", log)),
132 newCachedThreadPool(groupedThreads("onos/fpm", "sm-worker-%d", log)));
Jonathan Hart3930f632015-10-19 12:12:51 -0700133 ChannelPipelineFactory pipelineFactory = () -> {
134 // Allocate a new session per connection
135 FpmSessionHandler fpmSessionHandler =
136 new FpmSessionHandler(new InternalFpmListener());
137 FpmFrameDecoder fpmFrameDecoder =
138 new FpmFrameDecoder();
139
140 // Setup the processing pipeline
141 ChannelPipeline pipeline = Channels.pipeline();
142 pipeline.addLast("FpmFrameDecoder", fpmFrameDecoder);
143 pipeline.addLast("FpmSession", fpmSessionHandler);
144 return pipeline;
145 };
146
147 InetSocketAddress listenAddress = new InetSocketAddress(FPM_PORT);
148
149 serverBootstrap = new ServerBootstrap(channelFactory);
150 serverBootstrap.setOption("child.reuseAddr", true);
151 serverBootstrap.setOption("child.keepAlive", true);
152 serverBootstrap.setOption("child.tcpNoDelay", true);
153 serverBootstrap.setPipelineFactory(pipelineFactory);
154 try {
155 serverChannel = serverBootstrap.bind(listenAddress);
156 allChannels.add(serverChannel);
157 } catch (ChannelException e) {
158 log.debug("Exception binding to FPM port {}: ",
159 listenAddress.getPort(), e);
160 stopServer();
161 }
162 }
163
164 private void stopServer() {
165 allChannels.close().awaitUninterruptibly();
166 allChannels.clear();
167 if (serverBootstrap != null) {
168 serverBootstrap.releaseExternalResources();
169 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800170
171 if (clearRoutes) {
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700172 peers.keySet().forEach(this::clearRoutes);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800173 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700174 }
175
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700176 private void fpmMessage(FpmPeer peer, FpmHeader fpmMessage) {
Jonathan Hart3930f632015-10-19 12:12:51 -0700177 Netlink netlink = fpmMessage.netlink();
178 RtNetlink rtNetlink = netlink.rtNetlink();
179
180 if (log.isTraceEnabled()) {
181 log.trace("Received FPM message: {}", fpmMessage);
182 }
183
Jonathan Hartb3dad102016-03-01 17:42:26 -0800184 if (!(rtNetlink.protocol() == RtProtocol.ZEBRA ||
185 rtNetlink.protocol() == RtProtocol.UNSPEC)) {
Jonathan Hart916bf892016-01-27 16:42:55 -0800186 log.trace("Ignoring non-zebra route");
187 return;
188 }
189
Jonathan Hart3930f632015-10-19 12:12:51 -0700190 IpAddress dstAddress = null;
191 IpAddress gateway = null;
192
193 for (RouteAttribute attribute : rtNetlink.attributes()) {
194 if (attribute.type() == RouteAttribute.RTA_DST) {
195 RouteAttributeDst raDst = (RouteAttributeDst) attribute;
196 dstAddress = raDst.dstAddress();
197 } else if (attribute.type() == RouteAttribute.RTA_GATEWAY) {
198 RouteAttributeGateway raGateway = (RouteAttributeGateway) attribute;
199 gateway = raGateway.gateway();
200 }
201 }
202
203 if (dstAddress == null) {
204 log.error("Dst address missing!");
205 return;
206 }
207
208 IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
209
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700210 List<Route> updates = new LinkedList<>();
211 List<Route> withdraws = new LinkedList<>();
212
213 Route route;
Jonathan Hart3930f632015-10-19 12:12:51 -0700214 switch (netlink.type()) {
215 case RTM_NEWROUTE:
216 if (gateway == null) {
217 // We ignore interface routes with no gateway for now.
218 return;
219 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700220 route = new Route(Route.Source.FPM, prefix, gateway);
Jonathan Hart3930f632015-10-19 12:12:51 -0700221
Jonathan Hart3930f632015-10-19 12:12:51 -0700222
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700223 Route oldRoute = fpmRoutes.get(peer).put(prefix, route);
224
225 if (oldRoute != null) {
226 log.trace("Swapping {} with {}", oldRoute, route);
227 withdraws.add(oldRoute);
228 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700229 updates.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700230 break;
231 case RTM_DELROUTE:
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700232 Route existing = fpmRoutes.get(peer).remove(prefix);
Jonathan Hart3930f632015-10-19 12:12:51 -0700233 if (existing == null) {
234 log.warn("Got delete for non-existent prefix");
235 return;
236 }
237
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700238 route = new Route(Route.Source.FPM, prefix, existing.nextHop());
Jonathan Hart3930f632015-10-19 12:12:51 -0700239
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700240 withdraws.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700241 break;
242 case RTM_GETROUTE:
243 default:
244 break;
245 }
246
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700247 routeService.withdraw(withdraws);
248 routeService.update(updates);
Jonathan Hart3930f632015-10-19 12:12:51 -0700249 }
250
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800251
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700252 private void clearRoutes(FpmPeer peer) {
253 log.info("Clearing all routes for peer {}", peer);
254 Map<IpPrefix, Route> routes = fpmRoutes.remove(peer);
255 if (routes != null) {
256 routeService.withdraw(routes.values());
257 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800258 }
259
Jonathan Hart6b045582016-02-03 10:00:08 -0800260 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700261 public Map<FpmPeer, Long> peers() {
Jonathan Hart6b045582016-02-03 10:00:08 -0800262 return ImmutableMap.copyOf(peers);
263 }
264
265 private class InternalFpmListener implements FpmListener {
Jonathan Hart3930f632015-10-19 12:12:51 -0700266 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700267 public void fpmMessage(FpmPeer peer, FpmHeader fpmMessage) {
268 FpmManager.this.fpmMessage(peer, fpmMessage);
Jonathan Hart3930f632015-10-19 12:12:51 -0700269 }
Jonathan Hart6b045582016-02-03 10:00:08 -0800270
271 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700272 public boolean peerConnected(FpmPeer peer) {
273 if (peers.keySet().contains(peer)) {
Jonathan Hart6b045582016-02-03 10:00:08 -0800274 return false;
275 }
276
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700277 peers.put(peer, System.currentTimeMillis());
278 fpmRoutes.computeIfAbsent(peer, p -> new ConcurrentHashMap<>());
Jonathan Hart6b045582016-02-03 10:00:08 -0800279 return true;
280 }
281
282 @Override
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700283 public void peerDisconnected(FpmPeer peer) {
284 log.info("FPM connection to {} went down", peer);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800285
286 if (clearRoutes) {
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700287 clearRoutes(peer);
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800288 }
289
Jonathan Hartb10f1e72017-05-02 16:36:26 -0700290 peers.remove(peer);
Jonathan Hart6b045582016-02-03 10:00:08 -0800291 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700292 }
293
294}