blob: bb1448f6994de8820a22164623490fdec81cdf55 [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 Hart1ad75f22016-04-13 21:24:13 -070019import com.google.common.collect.ImmutableList;
Jonathan Hart6b045582016-02-03 10:00:08 -080020import com.google.common.collect.ImmutableMap;
Jonathan Hart3930f632015-10-19 12:12:51 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080024import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Property;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Hart3930f632015-10-19 12:12:51 -070028import org.apache.felix.scr.annotations.Service;
29import org.jboss.netty.bootstrap.ServerBootstrap;
30import org.jboss.netty.channel.Channel;
31import org.jboss.netty.channel.ChannelException;
32import org.jboss.netty.channel.ChannelFactory;
33import org.jboss.netty.channel.ChannelPipeline;
34import org.jboss.netty.channel.ChannelPipelineFactory;
35import org.jboss.netty.channel.Channels;
36import org.jboss.netty.channel.group.ChannelGroup;
37import org.jboss.netty.channel.group.DefaultChannelGroup;
38import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
39import org.onlab.packet.IpAddress;
40import org.onlab.packet.IpPrefix;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080041import org.onlab.util.Tools;
42import org.onosproject.cfg.ComponentConfigService;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070043import org.onosproject.incubator.net.routing.Route;
44import org.onosproject.incubator.net.routing.RouteAdminService;
Jonathan Hart3930f632015-10-19 12:12:51 -070045import 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 Hart1af5a7a2016-02-15 08:51:27 -080058import java.util.Dictionary;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070059import java.util.LinkedList;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080060import java.util.List;
Jonathan Hart3930f632015-10-19 12:12:51 -070061import java.util.Map;
62import java.util.concurrent.ConcurrentHashMap;
63
64import static java.util.concurrent.Executors.newCachedThreadPool;
65import static org.onlab.util.Tools.groupedThreads;
66
67/**
68 * Forwarding Plane Manager (FPM) route source.
69 */
70@Service
Jonathan Hartf4bd0482017-01-27 15:11:18 -080071@Component(immediate = true)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070072public class FpmManager implements FpmInfoService {
Jonathan Hart3930f632015-10-19 12:12:51 -070073 private final Logger log = LoggerFactory.getLogger(getClass());
74
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080075 private static final int FPM_PORT = 2620;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected ComponentConfigService componentConfigService;
79
Jonathan Hart1ad75f22016-04-13 21:24:13 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected RouteAdminService routeService;
82
Jonathan Hart3930f632015-10-19 12:12:51 -070083 private ServerBootstrap serverBootstrap;
84 private Channel serverChannel;
85 private ChannelGroup allChannels = new DefaultChannelGroup();
86
Jonathan Hart6b045582016-02-03 10:00:08 -080087 private Map<SocketAddress, Long> peers = new ConcurrentHashMap<>();
88
Jonathan Hart1ad75f22016-04-13 21:24:13 -070089 private Map<IpPrefix, Route> fpmRoutes = new ConcurrentHashMap<>();
Jonathan Hart3930f632015-10-19 12:12:51 -070090
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) {
Charles Chane7926852017-02-02 11:41:19 -080097 componentConfigService.preSetProperty(
98 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
99 "distributed", "true");
100
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800101 componentConfigService.registerProperties(getClass());
102 modified(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700103 startServer();
Jonathan Hart3930f632015-10-19 12:12:51 -0700104 log.info("Started");
105 }
106
107 @Deactivate
108 protected void deactivate() {
Charles Chane7926852017-02-02 11:41:19 -0800109 componentConfigService.preSetProperty(
110 "org.onosproject.incubator.store.routing.impl.RouteStoreImpl",
111 "distributed", "false");
112
Jonathan Hart3930f632015-10-19 12:12:51 -0700113 stopServer();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700114 fpmRoutes.clear();
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800115 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart3930f632015-10-19 12:12:51 -0700116 log.info("Stopped");
117 }
118
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800119 @Modified
120 protected void modified(ComponentContext context) {
121 Dictionary<?, ?> properties = context.getProperties();
122 if (properties == null) {
123 return;
124 }
125 String strClearRoutes = Tools.get(properties, "clearRoutes");
126 clearRoutes = Boolean.parseBoolean(strClearRoutes);
127
128 log.info("clearRoutes set to {}", clearRoutes);
129 }
130
Jonathan Hart3930f632015-10-19 12:12:51 -0700131 private void startServer() {
132 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
Yuta HIGUCHI1624df12016-07-21 16:54:33 -0700133 newCachedThreadPool(groupedThreads("onos/fpm", "sm-boss-%d", log)),
134 newCachedThreadPool(groupedThreads("onos/fpm", "sm-worker-%d", log)));
Jonathan Hart3930f632015-10-19 12:12:51 -0700135 ChannelPipelineFactory pipelineFactory = () -> {
136 // Allocate a new session per connection
137 FpmSessionHandler fpmSessionHandler =
138 new FpmSessionHandler(new InternalFpmListener());
139 FpmFrameDecoder fpmFrameDecoder =
140 new FpmFrameDecoder();
141
142 // Setup the processing pipeline
143 ChannelPipeline pipeline = Channels.pipeline();
144 pipeline.addLast("FpmFrameDecoder", fpmFrameDecoder);
145 pipeline.addLast("FpmSession", fpmSessionHandler);
146 return pipeline;
147 };
148
149 InetSocketAddress listenAddress = new InetSocketAddress(FPM_PORT);
150
151 serverBootstrap = new ServerBootstrap(channelFactory);
152 serverBootstrap.setOption("child.reuseAddr", true);
153 serverBootstrap.setOption("child.keepAlive", true);
154 serverBootstrap.setOption("child.tcpNoDelay", true);
155 serverBootstrap.setPipelineFactory(pipelineFactory);
156 try {
157 serverChannel = serverBootstrap.bind(listenAddress);
158 allChannels.add(serverChannel);
159 } catch (ChannelException e) {
160 log.debug("Exception binding to FPM port {}: ",
161 listenAddress.getPort(), e);
162 stopServer();
163 }
164 }
165
166 private void stopServer() {
167 allChannels.close().awaitUninterruptibly();
168 allChannels.clear();
169 if (serverBootstrap != null) {
170 serverBootstrap.releaseExternalResources();
171 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800172
173 if (clearRoutes) {
174 clearRoutes();
175 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700176 }
177
Jonathan Hart3930f632015-10-19 12:12:51 -0700178 private void fpmMessage(FpmHeader fpmMessage) {
179 Netlink netlink = fpmMessage.netlink();
180 RtNetlink rtNetlink = netlink.rtNetlink();
181
182 if (log.isTraceEnabled()) {
183 log.trace("Received FPM message: {}", fpmMessage);
184 }
185
Jonathan Hartb3dad102016-03-01 17:42:26 -0800186 if (!(rtNetlink.protocol() == RtProtocol.ZEBRA ||
187 rtNetlink.protocol() == RtProtocol.UNSPEC)) {
Jonathan Hart916bf892016-01-27 16:42:55 -0800188 log.trace("Ignoring non-zebra route");
189 return;
190 }
191
Jonathan Hart3930f632015-10-19 12:12:51 -0700192 IpAddress dstAddress = null;
193 IpAddress gateway = null;
194
195 for (RouteAttribute attribute : rtNetlink.attributes()) {
196 if (attribute.type() == RouteAttribute.RTA_DST) {
197 RouteAttributeDst raDst = (RouteAttributeDst) attribute;
198 dstAddress = raDst.dstAddress();
199 } else if (attribute.type() == RouteAttribute.RTA_GATEWAY) {
200 RouteAttributeGateway raGateway = (RouteAttributeGateway) attribute;
201 gateway = raGateway.gateway();
202 }
203 }
204
205 if (dstAddress == null) {
206 log.error("Dst address missing!");
207 return;
208 }
209
210 IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
211
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700212 List<Route> updates = new LinkedList<>();
213 List<Route> withdraws = new LinkedList<>();
214
215 Route route;
Jonathan Hart3930f632015-10-19 12:12:51 -0700216 switch (netlink.type()) {
217 case RTM_NEWROUTE:
218 if (gateway == null) {
219 // We ignore interface routes with no gateway for now.
220 return;
221 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700222 route = new Route(Route.Source.FPM, prefix, gateway);
Jonathan Hart3930f632015-10-19 12:12:51 -0700223
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700224 fpmRoutes.put(prefix, route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700225
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700226 updates.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700227 break;
228 case RTM_DELROUTE:
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700229 Route existing = fpmRoutes.remove(prefix);
Jonathan Hart3930f632015-10-19 12:12:51 -0700230 if (existing == null) {
231 log.warn("Got delete for non-existent prefix");
232 return;
233 }
234
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700235 route = new Route(Route.Source.FPM, prefix, existing.nextHop());
Jonathan Hart3930f632015-10-19 12:12:51 -0700236
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700237 withdraws.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700238 break;
239 case RTM_GETROUTE:
240 default:
241 break;
242 }
243
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700244 routeService.withdraw(withdraws);
245 routeService.update(updates);
Jonathan Hart3930f632015-10-19 12:12:51 -0700246 }
247
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800248
249 private void clearRoutes() {
250 log.info("Clearing all routes");
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700251 routeService.withdraw(ImmutableList.copyOf(fpmRoutes.values()));
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800252 }
253
Jonathan Hart6b045582016-02-03 10:00:08 -0800254 @Override
255 public Map<SocketAddress, Long> peers() {
256 return ImmutableMap.copyOf(peers);
257 }
258
259 private class InternalFpmListener implements FpmListener {
Jonathan Hart3930f632015-10-19 12:12:51 -0700260 @Override
261 public void fpmMessage(FpmHeader fpmMessage) {
262 FpmManager.this.fpmMessage(fpmMessage);
263 }
Jonathan Hart6b045582016-02-03 10:00:08 -0800264
265 @Override
266 public boolean peerConnected(SocketAddress address) {
267 if (peers.keySet().contains(address)) {
268 return false;
269 }
270
271 peers.put(address, System.currentTimeMillis());
272 return true;
273 }
274
275 @Override
276 public void peerDisconnected(SocketAddress address) {
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800277 log.info("FPM connection to {} went down", address);
278
279 if (clearRoutes) {
280 clearRoutes();
281 }
282
Jonathan Hart6b045582016-02-03 10:00:08 -0800283 peers.remove(address);
284 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700285 }
286
287}