blob: 1c08c07b364d2cdd41856fa11af82bc902146478 [file] [log] [blame]
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -04001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
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 */
16
17package org.onosproject.ra;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050022import org.apache.felix.scr.annotations.Modified;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040023import org.apache.felix.scr.annotations.Property;
24import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
26import org.onlab.packet.EthType;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.ICMP6;
29import org.onlab.packet.IPv6;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040030import org.onlab.packet.Ip6Address;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050031import org.onlab.packet.IpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040032import org.onlab.packet.MacAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040033import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050034import org.onlab.packet.ndp.RouterAdvertisement;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040035import org.onosproject.cfg.ComponentConfigService;
36import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.mastership.MastershipService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050039import org.onosproject.net.ConnectPoint;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040040import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040041import org.onosproject.net.MastershipRole;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040042import org.onosproject.net.config.ConfigFactory;
43import org.onosproject.net.config.NetworkConfigEvent;
44import org.onosproject.net.config.NetworkConfigListener;
45import org.onosproject.net.config.NetworkConfigRegistry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050046import org.onosproject.net.config.basics.InterfaceConfig;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040047import org.onosproject.net.config.basics.SubjectFactories;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050048import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040050import org.onosproject.net.device.DeviceService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050051import org.onosproject.net.flow.DefaultTrafficTreatment;
52import org.onosproject.net.flow.TrafficTreatment;
53import org.onosproject.net.host.InterfaceIpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040054import org.onosproject.net.intf.Interface;
55import org.onosproject.net.intf.InterfaceEvent;
56import org.onosproject.net.intf.InterfaceListener;
57import org.onosproject.net.intf.InterfaceService;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040058import org.onosproject.net.packet.DefaultOutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050059import org.onosproject.net.packet.InboundPacket;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040060import org.onosproject.net.packet.OutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050061import org.onosproject.net.packet.PacketContext;
62import org.onosproject.net.packet.PacketProcessor;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040063import org.onosproject.net.packet.PacketService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040064import org.onosproject.ra.config.RouterAdvertisementDeviceConfig;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050065import org.osgi.service.component.ComponentContext;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040066import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040068
69import javax.annotation.concurrent.GuardedBy;
70import java.nio.ByteBuffer;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050071import java.util.AbstractMap;
72import java.util.Arrays;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040073import java.util.Dictionary;
74import java.util.LinkedHashMap;
75import java.util.List;
76import java.util.Map;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040077import java.util.Optional;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040078import java.util.Set;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050079import java.util.concurrent.CompletableFuture;
80import java.util.concurrent.Executors;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040081import java.util.concurrent.ScheduledExecutorService;
82import java.util.concurrent.ScheduledFuture;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040083import java.util.concurrent.TimeUnit;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040084import java.util.function.Function;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040085import java.util.stream.IntStream;
86
87import static com.google.common.base.Strings.isNullOrEmpty;
88import static org.onlab.util.Tools.get;
89import static org.onlab.util.Tools.groupedThreads;
90
91/**
92 * Manages IPv6 Router Advertisements.
93 */
94@Component(immediate = true)
95public class RouterAdvertisementManager {
96
97 private final Logger log = LoggerFactory.getLogger(getClass());
98 private static final String PROP_RA_THREADS_POOL = "raPoolSize";
99 private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
100 private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
101 private static final int DEFAULT_RA_THREADS_DELAY = 5;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400102 private static final String PROP_RA_FLAG_MBIT_STATUS = "raFlagMbitStatus";
103 private static final boolean DEFAULT_RA_FLAG_MBIT_STATUS = false;
104 private static final String PROP_RA_FLAG_OBIT_STATUS = "raFlagObitStatus";
105 private static final boolean DEFAULT_RA_FLAG_OBIT_STATUS = false;
106 private static final String PROP_RA_OPTION_PREFIX_STATUS = "raOptionPrefixStatus";
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400107 private static final boolean DEFAULT_RA_OPTION_PREFIX_STATUS = false;
108 private static final String PROP_RA_GLOBAL_PREFIX_CONF_STATUS = "raGlobalPrefixConfStatus";
109 private static final boolean DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS = true;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 PacketService packetService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected ComponentConfigService componentConfigService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 public InterfaceService interfaceService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 public MastershipService mastershipService;
125
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500127 protected NetworkConfigRegistry networkConfigRegistry;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500130 protected DeviceService deviceService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400131
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400132 @Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400133 label = "Thread pool capacity")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400134 protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
135
136 @Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400137 label = "Thread delay in seconds")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400138 protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
139
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400140 @Property(name = PROP_RA_FLAG_MBIT_STATUS, boolValue = DEFAULT_RA_FLAG_MBIT_STATUS,
141 label = "Turn M-bit flag on/off")
142 protected boolean raFlagMbitStatus = DEFAULT_RA_FLAG_MBIT_STATUS;
143
144 @Property(name = PROP_RA_FLAG_OBIT_STATUS, boolValue = DEFAULT_RA_FLAG_OBIT_STATUS,
145 label = "Turn O-bit flag on/off")
146 protected boolean raFlagObitStatus = DEFAULT_RA_FLAG_OBIT_STATUS;
147
148 @Property(name = PROP_RA_OPTION_PREFIX_STATUS, boolValue = DEFAULT_RA_OPTION_PREFIX_STATUS,
149 label = "Prefix option support needed or not")
150 protected boolean raOptionPrefixStatus = DEFAULT_RA_OPTION_PREFIX_STATUS;
151
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400152 @Property(name = PROP_RA_GLOBAL_PREFIX_CONF_STATUS, boolValue = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS,
153 label = "Global prefix configuration support on/off")
154 protected boolean raGlobalConfigStatus = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400155
156 @GuardedBy(value = "this")
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500157 private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
158 new LinkedHashMap<>();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400159
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400160 @GuardedBy(value = "this")
161 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
162
163 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
164 i -> {
165 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
166 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
167 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
168 globalPrefixes.get(i.connectPoint().deviceId()));
169 } else {
170 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
171 }
172 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500173 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400174
175 private ScheduledExecutorService executors = null;
176
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400177 private static final String APP_NAME = "org.onosproject.routeradvertisement";
178 private ApplicationId appId;
179
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400180 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
181 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
182 SubjectFactories.DEVICE_SUBJECT_FACTORY,
183 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
184 @Override
185 public RouterAdvertisementDeviceConfig createConfig() {
186
187 return new RouterAdvertisementDeviceConfig();
188 }
189 };
190
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400191 // Listener for handling dynamic interface modifications.
192 private class InternalInterfaceListener implements InterfaceListener {
193 @Override
194 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400195 switch (event.type()) {
196 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500197 case INTERFACE_UPDATED:
198 clearTxWorkers();
199 loadGlobalPrefixConfig();
200 setupTxWorkers();
201 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400202 break;
203 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500204 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400205 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
206 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500207 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400208 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400209 break;
210 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400211 }
212 }
213 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400214
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400215 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
216
217 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400218 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
219 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500220 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400221 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
222 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500223 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400224 }
225
226 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500227 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400228 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500229 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
230 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400231 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500232 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400233 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500234 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400235 }
236
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400237 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400238 executors = Executors.newScheduledThreadPool(raPoolSize,
239 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400240 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400241
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400242 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400243 executors.shutdown();
244 }
245
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400246 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400247 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400248 interfaceService.getInterfaces()
249 .stream()
250 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
251 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400252 .map(prefixGenerator::apply)
253 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400254 .stream()
255 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
256 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400257 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400258 );
259 }
260
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400261 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400262 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500263 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400264 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400265 }
266
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400267 private synchronized void setupPoolAndTxWorkers() {
268 setupThreadPool();
269 setupTxWorkers();
270 }
271
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400272 private synchronized void clearPoolAndTxWorkers() {
273 clearTxWorkers();
274 clearThreadPool();
275 }
276
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400277 // Loading global prefixes for devices from network configuration
278 private synchronized void loadGlobalPrefixConfig() {
279 globalPrefixes.clear();
280 Set<DeviceId> deviceSubjects =
281 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
282 deviceSubjects.forEach(subject -> {
283 RouterAdvertisementDeviceConfig config =
284 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
285 if (config != null) {
286 List<InterfaceIpAddress> ips = config.prefixes();
287 globalPrefixes.put(subject, ips);
288 }
289 });
290 }
291
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500292 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400293 private class InternalNetworkConfigListener implements NetworkConfigListener {
294 @Override
295 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500296 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
297 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400298 switch (event.type()) {
299 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400300 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400301 clearTxWorkers();
302 loadGlobalPrefixConfig();
303 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500304 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400305 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500306 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400307 }
308 }
309 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400310 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400311
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500312 private final InternalNetworkConfigListener networkConfigListener
313 = new InternalNetworkConfigListener();
314
315 // Handler for device updates
316 private class InternalDeviceListener implements DeviceListener {
317
318 @Override
319 public void event(DeviceEvent event) {
320 switch (event.type()) {
321 case DEVICE_ADDED:
322 case PORT_UPDATED:
323 case PORT_ADDED:
324 case DEVICE_UPDATED:
325 case DEVICE_AVAILABILITY_CHANGED:
326 clearTxWorkers();
327 setupTxWorkers();
328 log.trace("Processed device event {} on {}", event.type(), event.subject());
329 break;
330 default:
331 }
332 }
333 }
334
335 private final InternalDeviceListener internalDeviceListener =
336 new InternalDeviceListener();
337
338 // Processor for Solicited RA packets
339 private class InternalPacketProcessor implements PacketProcessor {
340
341 @Override
342 public void process(PacketContext context) {
343 if (context.isHandled()) {
344 return;
345 }
346
347 // Ensure packet is IPv6 Solicited RA
348 InboundPacket pkt = context.inPacket();
349 Ethernet ethernet = pkt.parsed();
350 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
351 return;
352 }
353 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
354 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
355 return;
356 }
357 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
358 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
359 return;
360 }
361
362 // Start solicited-RA handling thread
363 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
364 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
365 }
366 }
367
368 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400369
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400370 @Activate
371 protected void activate(ComponentContext context) {
372 // Basic application registrations.
373 appId = coreService.registerApplication(APP_NAME);
374 componentConfigService.registerProperties(getClass());
375
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500376 // Packet processor for handling Router Solicitations
377 processor = new InternalPacketProcessor();
378 packetService.addProcessor(processor, PacketProcessor.director(3));
379
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400380 // Setup global prefix loading components
381 networkConfigRegistry.addListener(networkConfigListener);
382 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
383 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400384
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500385 // Dynamic device updates handling
386 deviceService.addListener(internalDeviceListener);
387
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400388 // Setup pool and worker threads for existing interfaces
389 setupPoolAndTxWorkers();
390 }
391
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400392
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400393 @Modified
394 protected void modified(ComponentContext context) {
395 int newRaPoolSize, newRaThreadDelay;
396
397 // Loading configured properties.
398 if (context != null) {
399 Dictionary<?, ?> properties = context.getProperties();
400 try {
401 // Handle change in pool size
402 String s = get(properties, PROP_RA_THREADS_POOL);
403 newRaPoolSize = isNullOrEmpty(s) ?
404 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
405 if (newRaPoolSize != raPoolSize) {
406 raPoolSize = newRaPoolSize;
407 clearPoolAndTxWorkers();
408 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400409 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400410 }
411
412 // Handle change in thread delay
413 s = get(properties, PROP_RA_THREADS_DELAY);
414 newRaThreadDelay = isNullOrEmpty(s) ?
415 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
416 if (newRaThreadDelay != raThreadDelay) {
417 raThreadDelay = newRaThreadDelay;
418 clearTxWorkers();
419 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400420 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400421 }
422
423 // Handle M-flag changes
424 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400425 if (!isNullOrEmpty(s)) {
426 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
427 log.info("RA M-flag set {}", s);
428 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400429
430 // Handle O-flag changes
431 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400432 if (!isNullOrEmpty(s)) {
433 raFlagObitStatus = Boolean.parseBoolean(s.trim());
434 log.info("RA O-flag set {}", s);
435 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400436
437 // Handle prefix option configuration
438 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400439 if (!isNullOrEmpty(s)) {
440 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
441 String status = raOptionPrefixStatus ? "enabled" : "disabled";
442 log.info("RA prefix option {}", status);
443 }
444
445 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
446 if (!isNullOrEmpty(s)) {
447 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
448 clearTxWorkers();
449 setupTxWorkers();
450 String status = raOptionPrefixStatus ? "enabled" : "disabled";
451 log.info("RA global configuration file loading {}", status);
452 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400453
454 } catch (NumberFormatException e) {
455 log.warn("Component configuration had invalid value, aborting changes loading.", e);
456 }
457 }
458 }
459
460 @Deactivate
461 protected void deactivate() {
462 // Unregister resources.
463 componentConfigService.unregisterProperties(getClass(), false);
464 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500465 networkConfigRegistry.removeListener(networkConfigListener);
466 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
467 packetService.removeProcessor(processor);
468 deviceService.removeListener(internalDeviceListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400469
470 // Clear pool & threads
471 clearPoolAndTxWorkers();
472 }
473
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400474 // Worker thread for actually sending ICMPv6 RA packets.
475 private class RAWorkerThread implements Runnable {
476
477 ConnectPoint connectPoint;
478 List<InterfaceIpAddress> ipAddresses;
479 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500480 MacAddress solicitHostMac;
481 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400482
483 // Various fixed values in RA packet
484 public static final byte RA_HOP_LIMIT = (byte) 0xff;
485 public static final short RA_ROUTER_LIFETIME = (short) 1800;
486 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
487 public static final int RA_OPTION_MTU_VALUE = 1500;
488 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
489 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
490 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
491
492
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500493 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
494 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400495 this.connectPoint = connectPoint;
496 this.ipAddresses = ipAddresses;
497 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500498 solicitHostMac = macAddress;
499 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400500 }
501
502 public void run() {
503 // Router Advertisement header filling. Please refer RFC-2461.
504 RouterAdvertisement ra = new RouterAdvertisement();
505 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400506 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
507 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400508 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
509 ra.setReachableTime(0);
510 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
511
512 // Option : Source link-layer address.
513 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
514 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
515 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
516 .map(Interface::mac).findFirst();
517 if (!macAddress.isPresent()) {
518 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
519 return;
520 }
521 option.put(macAddress.get().toBytes());
522 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
523 Arrays.copyOfRange(option.array(), 0, option.position()));
524
525 // Option : MTU.
526 option.rewind();
527 option.putShort((short) 0);
528 option.putInt(RA_OPTION_MTU_VALUE);
529 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
530 Arrays.copyOfRange(option.array(), 0, option.position()));
531
532 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400533 if (raOptionPrefixStatus) {
534 ipAddresses.stream()
535 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
536 .forEach(i -> {
537 option.rewind();
538 option.put((byte) i.subnetAddress().prefixLength());
539 // Enable "onlink" option only.
540 option.put((byte) 0x80);
541 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
542 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
543 // Clear reserved fields
544 option.putInt(0x00000000);
545 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
546 i.subnetAddress().prefixLength()).toOctets());
547 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
548 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400549
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400550 });
551 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400552
553 // ICMPv6 header filling.
554 ICMP6 icmpv6 = new ICMP6();
555 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
556 icmpv6.setIcmpCode((byte) 0);
557 icmpv6.setPayload(ra);
558
559 // IPv6 header filling.
560 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
561 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500562 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400563 /* RA packet L2 source address created from port MAC address.
564 * Note : As per RFC-4861 RAs should be sent from link-local address.
565 */
566 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
567 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
568 ipv6.setHopLimit(RA_HOP_LIMIT);
569 ipv6.setTrafficClass((byte) 0xe0);
570 ipv6.setPayload(icmpv6);
571
572 // Ethernet header filling.
573 Ethernet ethernet = new Ethernet();
574
575 /* Ethernet IPv6 multicast address creation.
576 * Refer : RFC 2624 section 7.
577 */
578 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
579 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
580 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500581 // Provide unicast address for Solicit RA replays
582 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
583 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400584 ethernet.setSourceMACAddress(macAddress.get().toBytes());
585 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
586 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
587 ethernet.setPayload(ipv6);
588 ethernet.setPad(false);
589
590 // Flush out PACKET_OUT.
591 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
592 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
593 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
594 treatment, stream);
595 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500596 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400597 }
598 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500599
600 // Worker thread for processing IPv6 Solicited RA packets.
601 public class SolicitedRAWorkerThread implements Runnable {
602
603 private InboundPacket packet;
604
605 SolicitedRAWorkerThread(InboundPacket packet) {
606 this.packet = packet;
607 }
608
609 @Override
610 public void run() {
611
612 // TODO : Validate Router Solicitation
613
614 // Pause already running unsolicited RA threads in received connect point
615 ConnectPoint connectPoint = packet.receivedFrom();
616 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
617
618 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
619 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
620 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
621 */
622 log.trace("Processing Router Solicitations from {}", connectPoint);
623 try {
624 Ethernet ethernet = packet.parsed();
625 IPv6 ipv6 = (IPv6) ethernet.getPayload();
626 RAWorkerThread worker = new RAWorkerThread(connectPoint,
627 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
628 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
629 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
630 sraHandlerFuture.get();
631 } catch (Exception e) {
632 log.error("Failed to respond to router solicitation. {}", e);
633 } finally {
634 activateRouterAdvertisement(connectPoint, addresses);
635 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
636 }
637 }
638 }
639
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400640}