blob: 5f31bdf04897b4d845be8827d278d54b791ff064 [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;
Annce John0b4057a2017-11-21 01:46:41 -050021import org.apache.felix.scr.annotations.Service;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040022import org.apache.felix.scr.annotations.Deactivate;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050023import org.apache.felix.scr.annotations.Modified;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040024import org.apache.felix.scr.annotations.Property;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.EthType;
28import org.onlab.packet.Ethernet;
29import org.onlab.packet.ICMP6;
30import org.onlab.packet.IPv6;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040031import org.onlab.packet.Ip6Address;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050032import org.onlab.packet.IpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040033import org.onlab.packet.MacAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040034import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050035import org.onlab.packet.ndp.RouterAdvertisement;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040036import org.onosproject.cfg.ComponentConfigService;
37import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.mastership.MastershipService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050040import org.onosproject.net.ConnectPoint;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040041import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040042import org.onosproject.net.MastershipRole;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040043import org.onosproject.net.config.ConfigFactory;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050047import org.onosproject.net.config.basics.InterfaceConfig;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040048import org.onosproject.net.config.basics.SubjectFactories;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050049import org.onosproject.net.device.DeviceEvent;
50import org.onosproject.net.device.DeviceListener;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040051import org.onosproject.net.device.DeviceService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050052import org.onosproject.net.flow.DefaultTrafficTreatment;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.host.InterfaceIpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040055import org.onosproject.net.intf.Interface;
56import org.onosproject.net.intf.InterfaceEvent;
57import org.onosproject.net.intf.InterfaceListener;
58import org.onosproject.net.intf.InterfaceService;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040059import org.onosproject.net.packet.DefaultOutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050060import org.onosproject.net.packet.InboundPacket;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040061import org.onosproject.net.packet.OutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050062import org.onosproject.net.packet.PacketContext;
63import org.onosproject.net.packet.PacketProcessor;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040064import org.onosproject.net.packet.PacketService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040065import org.onosproject.ra.config.RouterAdvertisementDeviceConfig;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050066import org.osgi.service.component.ComponentContext;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040069
70import javax.annotation.concurrent.GuardedBy;
71import java.nio.ByteBuffer;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050072import java.util.AbstractMap;
73import java.util.Arrays;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040074import java.util.Dictionary;
75import java.util.LinkedHashMap;
76import java.util.List;
77import java.util.Map;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040078import java.util.Optional;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040079import java.util.Set;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050080import java.util.concurrent.CompletableFuture;
81import java.util.concurrent.Executors;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040082import java.util.concurrent.ScheduledExecutorService;
83import java.util.concurrent.ScheduledFuture;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040084import java.util.concurrent.TimeUnit;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040085import java.util.function.Function;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040086import java.util.stream.IntStream;
87
88import static com.google.common.base.Strings.isNullOrEmpty;
89import static org.onlab.util.Tools.get;
90import static org.onlab.util.Tools.groupedThreads;
Annce John0b4057a2017-11-21 01:46:41 -050091import com.google.common.collect.ImmutableMap;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040092
93/**
94 * Manages IPv6 Router Advertisements.
95 */
Annce John0b4057a2017-11-21 01:46:41 -050096@Service
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040097@Component(immediate = true)
Annce John0b4057a2017-11-21 01:46:41 -050098public class RouterAdvertisementManager implements RoutingAdvertisementService {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040099
100 private final Logger log = LoggerFactory.getLogger(getClass());
101 private static final String PROP_RA_THREADS_POOL = "raPoolSize";
102 private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
103 private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
104 private static final int DEFAULT_RA_THREADS_DELAY = 5;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400105 private static final String PROP_RA_FLAG_MBIT_STATUS = "raFlagMbitStatus";
106 private static final boolean DEFAULT_RA_FLAG_MBIT_STATUS = false;
107 private static final String PROP_RA_FLAG_OBIT_STATUS = "raFlagObitStatus";
108 private static final boolean DEFAULT_RA_FLAG_OBIT_STATUS = false;
109 private static final String PROP_RA_OPTION_PREFIX_STATUS = "raOptionPrefixStatus";
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400110 private static final boolean DEFAULT_RA_OPTION_PREFIX_STATUS = false;
111 private static final String PROP_RA_GLOBAL_PREFIX_CONF_STATUS = "raGlobalPrefixConfStatus";
112 private static final boolean DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS = true;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected CoreService coreService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 PacketService packetService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected ComponentConfigService componentConfigService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 public InterfaceService interfaceService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 public MastershipService mastershipService;
128
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500130 protected NetworkConfigRegistry networkConfigRegistry;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400131
132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500133 protected DeviceService deviceService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400134
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400135 @Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400136 label = "Thread pool capacity")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400137 protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
138
139 @Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400140 label = "Thread delay in seconds")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400141 protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
142
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400143 @Property(name = PROP_RA_FLAG_MBIT_STATUS, boolValue = DEFAULT_RA_FLAG_MBIT_STATUS,
144 label = "Turn M-bit flag on/off")
145 protected boolean raFlagMbitStatus = DEFAULT_RA_FLAG_MBIT_STATUS;
146
147 @Property(name = PROP_RA_FLAG_OBIT_STATUS, boolValue = DEFAULT_RA_FLAG_OBIT_STATUS,
148 label = "Turn O-bit flag on/off")
149 protected boolean raFlagObitStatus = DEFAULT_RA_FLAG_OBIT_STATUS;
150
151 @Property(name = PROP_RA_OPTION_PREFIX_STATUS, boolValue = DEFAULT_RA_OPTION_PREFIX_STATUS,
152 label = "Prefix option support needed or not")
153 protected boolean raOptionPrefixStatus = DEFAULT_RA_OPTION_PREFIX_STATUS;
154
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400155 @Property(name = PROP_RA_GLOBAL_PREFIX_CONF_STATUS, boolValue = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS,
156 label = "Global prefix configuration support on/off")
157 protected boolean raGlobalConfigStatus = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400158
159 @GuardedBy(value = "this")
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500160 private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
161 new LinkedHashMap<>();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400162
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400163 @GuardedBy(value = "this")
164 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
165
Annce John0b4057a2017-11-21 01:46:41 -0500166 @Override
167 public ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
168 return ImmutableMap.copyOf(globalPrefixes);
169 }
170
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400171 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
172 i -> {
173 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
174 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
175 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
176 globalPrefixes.get(i.connectPoint().deviceId()));
177 } else {
178 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
179 }
180 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500181 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400182
183 private ScheduledExecutorService executors = null;
184
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400185 private static final String APP_NAME = "org.onosproject.routeradvertisement";
186 private ApplicationId appId;
187
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400188 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
189 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
190 SubjectFactories.DEVICE_SUBJECT_FACTORY,
191 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
192 @Override
193 public RouterAdvertisementDeviceConfig createConfig() {
194
195 return new RouterAdvertisementDeviceConfig();
196 }
197 };
198
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400199 // Listener for handling dynamic interface modifications.
200 private class InternalInterfaceListener implements InterfaceListener {
201 @Override
202 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400203 switch (event.type()) {
204 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500205 case INTERFACE_UPDATED:
206 clearTxWorkers();
207 loadGlobalPrefixConfig();
208 setupTxWorkers();
209 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400210 break;
211 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500212 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400213 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
214 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500215 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400216 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400217 break;
218 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400219 }
220 }
221 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400222
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400223 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
224
225 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400226 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
227 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500228 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400229 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
230 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500231 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400232 }
233
234 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500235 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400236 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500237 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
238 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400239 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500240 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400241 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500242 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400243 }
244
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400245 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400246 executors = Executors.newScheduledThreadPool(raPoolSize,
247 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400248 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400249
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400250 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400251 executors.shutdown();
252 }
253
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400254 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400255 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400256 interfaceService.getInterfaces()
257 .stream()
258 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
259 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400260 .map(prefixGenerator::apply)
261 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400262 .stream()
263 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
264 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400265 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400266 );
267 }
268
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400269 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400270 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500271 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400272 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400273 }
274
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400275 private synchronized void setupPoolAndTxWorkers() {
276 setupThreadPool();
277 setupTxWorkers();
278 }
279
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400280 private synchronized void clearPoolAndTxWorkers() {
281 clearTxWorkers();
282 clearThreadPool();
283 }
284
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400285 // Loading global prefixes for devices from network configuration
286 private synchronized void loadGlobalPrefixConfig() {
287 globalPrefixes.clear();
288 Set<DeviceId> deviceSubjects =
289 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
290 deviceSubjects.forEach(subject -> {
291 RouterAdvertisementDeviceConfig config =
292 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
293 if (config != null) {
294 List<InterfaceIpAddress> ips = config.prefixes();
295 globalPrefixes.put(subject, ips);
296 }
297 });
298 }
299
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500300 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400301 private class InternalNetworkConfigListener implements NetworkConfigListener {
302 @Override
303 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500304 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
305 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400306 switch (event.type()) {
307 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400308 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400309 clearTxWorkers();
310 loadGlobalPrefixConfig();
311 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500312 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400313 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500314 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400315 }
316 }
317 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400318 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400319
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500320 private final InternalNetworkConfigListener networkConfigListener
321 = new InternalNetworkConfigListener();
322
323 // Handler for device updates
324 private class InternalDeviceListener implements DeviceListener {
325
326 @Override
327 public void event(DeviceEvent event) {
328 switch (event.type()) {
329 case DEVICE_ADDED:
330 case PORT_UPDATED:
331 case PORT_ADDED:
332 case DEVICE_UPDATED:
333 case DEVICE_AVAILABILITY_CHANGED:
334 clearTxWorkers();
335 setupTxWorkers();
336 log.trace("Processed device event {} on {}", event.type(), event.subject());
337 break;
338 default:
339 }
340 }
341 }
342
343 private final InternalDeviceListener internalDeviceListener =
344 new InternalDeviceListener();
345
346 // Processor for Solicited RA packets
347 private class InternalPacketProcessor implements PacketProcessor {
348
349 @Override
350 public void process(PacketContext context) {
351 if (context.isHandled()) {
352 return;
353 }
354
355 // Ensure packet is IPv6 Solicited RA
356 InboundPacket pkt = context.inPacket();
357 Ethernet ethernet = pkt.parsed();
358 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
359 return;
360 }
361 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
362 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
363 return;
364 }
365 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
366 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
367 return;
368 }
369
370 // Start solicited-RA handling thread
371 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
372 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
373 }
374 }
375
376 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400377
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400378 @Activate
379 protected void activate(ComponentContext context) {
380 // Basic application registrations.
381 appId = coreService.registerApplication(APP_NAME);
382 componentConfigService.registerProperties(getClass());
383
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500384 // Packet processor for handling Router Solicitations
385 processor = new InternalPacketProcessor();
386 packetService.addProcessor(processor, PacketProcessor.director(3));
387
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400388 // Setup global prefix loading components
389 networkConfigRegistry.addListener(networkConfigListener);
390 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
391 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400392
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500393 // Dynamic device updates handling
394 deviceService.addListener(internalDeviceListener);
395
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400396 // Setup pool and worker threads for existing interfaces
397 setupPoolAndTxWorkers();
398 }
399
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400400
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400401 @Modified
402 protected void modified(ComponentContext context) {
403 int newRaPoolSize, newRaThreadDelay;
404
405 // Loading configured properties.
406 if (context != null) {
407 Dictionary<?, ?> properties = context.getProperties();
408 try {
409 // Handle change in pool size
410 String s = get(properties, PROP_RA_THREADS_POOL);
411 newRaPoolSize = isNullOrEmpty(s) ?
412 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
413 if (newRaPoolSize != raPoolSize) {
414 raPoolSize = newRaPoolSize;
415 clearPoolAndTxWorkers();
416 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400417 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400418 }
419
420 // Handle change in thread delay
421 s = get(properties, PROP_RA_THREADS_DELAY);
422 newRaThreadDelay = isNullOrEmpty(s) ?
423 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
424 if (newRaThreadDelay != raThreadDelay) {
425 raThreadDelay = newRaThreadDelay;
426 clearTxWorkers();
427 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400428 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400429 }
430
431 // Handle M-flag changes
432 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400433 if (!isNullOrEmpty(s)) {
434 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
435 log.info("RA M-flag set {}", s);
436 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400437
438 // Handle O-flag changes
439 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400440 if (!isNullOrEmpty(s)) {
441 raFlagObitStatus = Boolean.parseBoolean(s.trim());
442 log.info("RA O-flag set {}", s);
443 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400444
445 // Handle prefix option configuration
446 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400447 if (!isNullOrEmpty(s)) {
448 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
449 String status = raOptionPrefixStatus ? "enabled" : "disabled";
450 log.info("RA prefix option {}", status);
451 }
452
453 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
454 if (!isNullOrEmpty(s)) {
455 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
456 clearTxWorkers();
457 setupTxWorkers();
458 String status = raOptionPrefixStatus ? "enabled" : "disabled";
459 log.info("RA global configuration file loading {}", status);
460 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400461
462 } catch (NumberFormatException e) {
463 log.warn("Component configuration had invalid value, aborting changes loading.", e);
464 }
465 }
466 }
467
468 @Deactivate
469 protected void deactivate() {
470 // Unregister resources.
471 componentConfigService.unregisterProperties(getClass(), false);
472 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500473 networkConfigRegistry.removeListener(networkConfigListener);
474 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
475 packetService.removeProcessor(processor);
476 deviceService.removeListener(internalDeviceListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400477
478 // Clear pool & threads
479 clearPoolAndTxWorkers();
480 }
481
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400482 // Worker thread for actually sending ICMPv6 RA packets.
483 private class RAWorkerThread implements Runnable {
484
485 ConnectPoint connectPoint;
486 List<InterfaceIpAddress> ipAddresses;
487 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500488 MacAddress solicitHostMac;
489 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400490
491 // Various fixed values in RA packet
492 public static final byte RA_HOP_LIMIT = (byte) 0xff;
493 public static final short RA_ROUTER_LIFETIME = (short) 1800;
494 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
495 public static final int RA_OPTION_MTU_VALUE = 1500;
496 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
497 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
498 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
499
500
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500501 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
502 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400503 this.connectPoint = connectPoint;
504 this.ipAddresses = ipAddresses;
505 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500506 solicitHostMac = macAddress;
507 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400508 }
509
510 public void run() {
511 // Router Advertisement header filling. Please refer RFC-2461.
512 RouterAdvertisement ra = new RouterAdvertisement();
513 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400514 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
515 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400516 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
517 ra.setReachableTime(0);
518 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
519
520 // Option : Source link-layer address.
521 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
522 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
523 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
524 .map(Interface::mac).findFirst();
525 if (!macAddress.isPresent()) {
526 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
527 return;
528 }
529 option.put(macAddress.get().toBytes());
530 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
531 Arrays.copyOfRange(option.array(), 0, option.position()));
532
533 // Option : MTU.
534 option.rewind();
535 option.putShort((short) 0);
536 option.putInt(RA_OPTION_MTU_VALUE);
537 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
538 Arrays.copyOfRange(option.array(), 0, option.position()));
539
540 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400541 if (raOptionPrefixStatus) {
542 ipAddresses.stream()
543 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
544 .forEach(i -> {
545 option.rewind();
546 option.put((byte) i.subnetAddress().prefixLength());
547 // Enable "onlink" option only.
548 option.put((byte) 0x80);
549 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
550 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
551 // Clear reserved fields
552 option.putInt(0x00000000);
553 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
554 i.subnetAddress().prefixLength()).toOctets());
555 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
556 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400557
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400558 });
559 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400560
561 // ICMPv6 header filling.
562 ICMP6 icmpv6 = new ICMP6();
563 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
564 icmpv6.setIcmpCode((byte) 0);
565 icmpv6.setPayload(ra);
566
567 // IPv6 header filling.
568 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
569 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500570 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400571 /* RA packet L2 source address created from port MAC address.
572 * Note : As per RFC-4861 RAs should be sent from link-local address.
573 */
574 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
575 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
576 ipv6.setHopLimit(RA_HOP_LIMIT);
577 ipv6.setTrafficClass((byte) 0xe0);
578 ipv6.setPayload(icmpv6);
579
580 // Ethernet header filling.
581 Ethernet ethernet = new Ethernet();
582
583 /* Ethernet IPv6 multicast address creation.
584 * Refer : RFC 2624 section 7.
585 */
586 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
587 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
588 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500589 // Provide unicast address for Solicit RA replays
590 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
591 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400592 ethernet.setSourceMACAddress(macAddress.get().toBytes());
593 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
594 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
595 ethernet.setPayload(ipv6);
596 ethernet.setPad(false);
597
598 // Flush out PACKET_OUT.
599 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
600 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
601 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
602 treatment, stream);
603 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500604 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400605 }
606 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500607
608 // Worker thread for processing IPv6 Solicited RA packets.
609 public class SolicitedRAWorkerThread implements Runnable {
610
611 private InboundPacket packet;
612
613 SolicitedRAWorkerThread(InboundPacket packet) {
614 this.packet = packet;
615 }
616
617 @Override
618 public void run() {
619
620 // TODO : Validate Router Solicitation
621
622 // Pause already running unsolicited RA threads in received connect point
623 ConnectPoint connectPoint = packet.receivedFrom();
624 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
625
626 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
627 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
628 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
629 */
630 log.trace("Processing Router Solicitations from {}", connectPoint);
631 try {
632 Ethernet ethernet = packet.parsed();
633 IPv6 ipv6 = (IPv6) ethernet.getPayload();
634 RAWorkerThread worker = new RAWorkerThread(connectPoint,
635 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
636 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
637 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
638 sraHandlerFuture.get();
639 } catch (Exception e) {
640 log.error("Failed to respond to router solicitation. {}", e);
641 } finally {
642 activateRouterAdvertisement(connectPoint, addresses);
643 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
644 }
645 }
646 }
647
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400648}