blob: c30e71d831c9e700365d9cabaa2e6c1b9430982d [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 Hart1ad75f22016-04-13 21:24:13 -070018import com.google.common.collect.ImmutableList;
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 Hart6b045582016-02-03 10:00:08 -080056import java.net.SocketAddress;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080057import java.util.Dictionary;
Jonathan Hart1ad75f22016-04-13 21:24:13 -070058import java.util.LinkedList;
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080059import java.util.List;
Jonathan Hart3930f632015-10-19 12:12:51 -070060import java.util.Map;
61import java.util.concurrent.ConcurrentHashMap;
62
63import static java.util.concurrent.Executors.newCachedThreadPool;
64import static org.onlab.util.Tools.groupedThreads;
65
66/**
67 * Forwarding Plane Manager (FPM) route source.
68 */
69@Service
70@Component(immediate = true, enabled = false)
Jonathan Hart1ad75f22016-04-13 21:24:13 -070071public class FpmManager implements FpmInfoService {
Jonathan Hart3930f632015-10-19 12:12:51 -070072 private final Logger log = LoggerFactory.getLogger(getClass());
73
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080074 private static final int FPM_PORT = 2620;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected ComponentConfigService componentConfigService;
78
Jonathan Hart1ad75f22016-04-13 21:24:13 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected RouteAdminService routeService;
81
Jonathan Hart3930f632015-10-19 12:12:51 -070082 private ServerBootstrap serverBootstrap;
83 private Channel serverChannel;
84 private ChannelGroup allChannels = new DefaultChannelGroup();
85
Jonathan Hart6b045582016-02-03 10:00:08 -080086 private Map<SocketAddress, Long> peers = new ConcurrentHashMap<>();
87
Jonathan Hart1ad75f22016-04-13 21:24:13 -070088 private Map<IpPrefix, Route> fpmRoutes = new ConcurrentHashMap<>();
Jonathan Hart3930f632015-10-19 12:12:51 -070089
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080090 @Property(name = "clearRoutes", boolValue = true,
91 label = "Whether to clear routes when the FPM connection goes down")
92 private boolean clearRoutes = true;
Jonathan Hart3930f632015-10-19 12:12:51 -070093
94 @Activate
Jonathan Hart1af5a7a2016-02-15 08:51:27 -080095 protected void activate(ComponentContext context) {
96 componentConfigService.registerProperties(getClass());
97 modified(context);
Jonathan Hart1ad75f22016-04-13 21:24:13 -070098 startServer();
Jonathan Hart3930f632015-10-19 12:12:51 -070099 log.info("Started");
100 }
101
102 @Deactivate
103 protected void deactivate() {
104 stopServer();
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700105 fpmRoutes.clear();
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800106 componentConfigService.unregisterProperties(getClass(), false);
Jonathan Hart3930f632015-10-19 12:12:51 -0700107 log.info("Stopped");
108 }
109
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800110 @Modified
111 protected void modified(ComponentContext context) {
112 Dictionary<?, ?> properties = context.getProperties();
113 if (properties == null) {
114 return;
115 }
116 String strClearRoutes = Tools.get(properties, "clearRoutes");
117 clearRoutes = Boolean.parseBoolean(strClearRoutes);
118
119 log.info("clearRoutes set to {}", clearRoutes);
120 }
121
Jonathan Hart3930f632015-10-19 12:12:51 -0700122 private void startServer() {
123 ChannelFactory channelFactory = new NioServerSocketChannelFactory(
124 newCachedThreadPool(groupedThreads("onos/fpm", "sm-boss-%d")),
125 newCachedThreadPool(groupedThreads("onos/fpm", "sm-worker-%d")));
126 ChannelPipelineFactory pipelineFactory = () -> {
127 // Allocate a new session per connection
128 FpmSessionHandler fpmSessionHandler =
129 new FpmSessionHandler(new InternalFpmListener());
130 FpmFrameDecoder fpmFrameDecoder =
131 new FpmFrameDecoder();
132
133 // Setup the processing pipeline
134 ChannelPipeline pipeline = Channels.pipeline();
135 pipeline.addLast("FpmFrameDecoder", fpmFrameDecoder);
136 pipeline.addLast("FpmSession", fpmSessionHandler);
137 return pipeline;
138 };
139
140 InetSocketAddress listenAddress = new InetSocketAddress(FPM_PORT);
141
142 serverBootstrap = new ServerBootstrap(channelFactory);
143 serverBootstrap.setOption("child.reuseAddr", true);
144 serverBootstrap.setOption("child.keepAlive", true);
145 serverBootstrap.setOption("child.tcpNoDelay", true);
146 serverBootstrap.setPipelineFactory(pipelineFactory);
147 try {
148 serverChannel = serverBootstrap.bind(listenAddress);
149 allChannels.add(serverChannel);
150 } catch (ChannelException e) {
151 log.debug("Exception binding to FPM port {}: ",
152 listenAddress.getPort(), e);
153 stopServer();
154 }
155 }
156
157 private void stopServer() {
158 allChannels.close().awaitUninterruptibly();
159 allChannels.clear();
160 if (serverBootstrap != null) {
161 serverBootstrap.releaseExternalResources();
162 }
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800163
164 if (clearRoutes) {
165 clearRoutes();
166 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700167 }
168
Jonathan Hart3930f632015-10-19 12:12:51 -0700169 private void fpmMessage(FpmHeader fpmMessage) {
170 Netlink netlink = fpmMessage.netlink();
171 RtNetlink rtNetlink = netlink.rtNetlink();
172
173 if (log.isTraceEnabled()) {
174 log.trace("Received FPM message: {}", fpmMessage);
175 }
176
Jonathan Hartb3dad102016-03-01 17:42:26 -0800177 if (!(rtNetlink.protocol() == RtProtocol.ZEBRA ||
178 rtNetlink.protocol() == RtProtocol.UNSPEC)) {
Jonathan Hart916bf892016-01-27 16:42:55 -0800179 log.trace("Ignoring non-zebra route");
180 return;
181 }
182
Jonathan Hart3930f632015-10-19 12:12:51 -0700183 IpAddress dstAddress = null;
184 IpAddress gateway = null;
185
186 for (RouteAttribute attribute : rtNetlink.attributes()) {
187 if (attribute.type() == RouteAttribute.RTA_DST) {
188 RouteAttributeDst raDst = (RouteAttributeDst) attribute;
189 dstAddress = raDst.dstAddress();
190 } else if (attribute.type() == RouteAttribute.RTA_GATEWAY) {
191 RouteAttributeGateway raGateway = (RouteAttributeGateway) attribute;
192 gateway = raGateway.gateway();
193 }
194 }
195
196 if (dstAddress == null) {
197 log.error("Dst address missing!");
198 return;
199 }
200
201 IpPrefix prefix = IpPrefix.valueOf(dstAddress, rtNetlink.dstLength());
202
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700203 List<Route> updates = new LinkedList<>();
204 List<Route> withdraws = new LinkedList<>();
205
206 Route route;
Jonathan Hart3930f632015-10-19 12:12:51 -0700207 switch (netlink.type()) {
208 case RTM_NEWROUTE:
209 if (gateway == null) {
210 // We ignore interface routes with no gateway for now.
211 return;
212 }
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700213 route = new Route(Route.Source.FPM, prefix, gateway);
Jonathan Hart3930f632015-10-19 12:12:51 -0700214
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700215 fpmRoutes.put(prefix, route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700216
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700217 updates.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700218 break;
219 case RTM_DELROUTE:
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700220 Route existing = fpmRoutes.remove(prefix);
Jonathan Hart3930f632015-10-19 12:12:51 -0700221 if (existing == null) {
222 log.warn("Got delete for non-existent prefix");
223 return;
224 }
225
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700226 route = new Route(Route.Source.FPM, prefix, existing.nextHop());
Jonathan Hart3930f632015-10-19 12:12:51 -0700227
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700228 withdraws.add(route);
Jonathan Hart3930f632015-10-19 12:12:51 -0700229 break;
230 case RTM_GETROUTE:
231 default:
232 break;
233 }
234
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700235 routeService.withdraw(withdraws);
236 routeService.update(updates);
Jonathan Hart3930f632015-10-19 12:12:51 -0700237 }
238
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800239
240 private void clearRoutes() {
241 log.info("Clearing all routes");
Jonathan Hart1ad75f22016-04-13 21:24:13 -0700242 routeService.withdraw(ImmutableList.copyOf(fpmRoutes.values()));
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800243 }
244
Jonathan Hart6b045582016-02-03 10:00:08 -0800245 @Override
246 public Map<SocketAddress, Long> peers() {
247 return ImmutableMap.copyOf(peers);
248 }
249
250 private class InternalFpmListener implements FpmListener {
Jonathan Hart3930f632015-10-19 12:12:51 -0700251 @Override
252 public void fpmMessage(FpmHeader fpmMessage) {
253 FpmManager.this.fpmMessage(fpmMessage);
254 }
Jonathan Hart6b045582016-02-03 10:00:08 -0800255
256 @Override
257 public boolean peerConnected(SocketAddress address) {
258 if (peers.keySet().contains(address)) {
259 return false;
260 }
261
262 peers.put(address, System.currentTimeMillis());
263 return true;
264 }
265
266 @Override
267 public void peerDisconnected(SocketAddress address) {
Jonathan Hart1af5a7a2016-02-15 08:51:27 -0800268 log.info("FPM connection to {} went down", address);
269
270 if (clearRoutes) {
271 clearRoutes();
272 }
273
Jonathan Hart6b045582016-02-03 10:00:08 -0800274 peers.remove(address);
275 }
Jonathan Hart3930f632015-10-19 12:12:51 -0700276 }
277
278}