blob: 9f10728457c1152d8b570f9eec0a0c136e6019d3 [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
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800163 // TODO: should consider using concurrent variants
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400164 @GuardedBy(value = "this")
165 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
166
Annce John0b4057a2017-11-21 01:46:41 -0500167 @Override
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800168 public synchronized ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
Annce John0b4057a2017-11-21 01:46:41 -0500169 return ImmutableMap.copyOf(globalPrefixes);
170 }
171
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800172 @SuppressWarnings("GuardedBy")
173 @GuardedBy(value = "this")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400174 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
175 i -> {
176 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
177 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
178 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
179 globalPrefixes.get(i.connectPoint().deviceId()));
180 } else {
181 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
182 }
183 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500184 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400185
186 private ScheduledExecutorService executors = null;
187
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400188 private static final String APP_NAME = "org.onosproject.routeradvertisement";
189 private ApplicationId appId;
190
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400191 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
192 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
193 SubjectFactories.DEVICE_SUBJECT_FACTORY,
194 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
195 @Override
196 public RouterAdvertisementDeviceConfig createConfig() {
197
198 return new RouterAdvertisementDeviceConfig();
199 }
200 };
201
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400202 // Listener for handling dynamic interface modifications.
203 private class InternalInterfaceListener implements InterfaceListener {
204 @Override
205 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400206 switch (event.type()) {
207 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500208 case INTERFACE_UPDATED:
209 clearTxWorkers();
210 loadGlobalPrefixConfig();
211 setupTxWorkers();
212 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400213 break;
214 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500215 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400216 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
217 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500218 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400219 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400220 break;
221 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400222 }
223 }
224 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400225
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400226 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
227
228 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400229 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
230 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500231 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400232 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
233 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500234 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400235 }
236
237 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500238 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400239 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500240 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
241 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400242 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500243 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400244 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500245 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400246 }
247
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400248 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400249 executors = Executors.newScheduledThreadPool(raPoolSize,
250 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400251 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400252
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400253 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400254 executors.shutdown();
255 }
256
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400257 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400258 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400259 interfaceService.getInterfaces()
260 .stream()
261 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
262 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400263 .map(prefixGenerator::apply)
264 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400265 .stream()
266 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
267 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400268 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400269 );
270 }
271
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400272 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400273 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500274 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400275 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400276 }
277
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400278 private synchronized void setupPoolAndTxWorkers() {
279 setupThreadPool();
280 setupTxWorkers();
281 }
282
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400283 private synchronized void clearPoolAndTxWorkers() {
284 clearTxWorkers();
285 clearThreadPool();
286 }
287
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800288 @SuppressWarnings("GuardedBy")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400289 // Loading global prefixes for devices from network configuration
290 private synchronized void loadGlobalPrefixConfig() {
291 globalPrefixes.clear();
292 Set<DeviceId> deviceSubjects =
293 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
294 deviceSubjects.forEach(subject -> {
295 RouterAdvertisementDeviceConfig config =
296 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
297 if (config != null) {
298 List<InterfaceIpAddress> ips = config.prefixes();
299 globalPrefixes.put(subject, ips);
300 }
301 });
302 }
303
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500304 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400305 private class InternalNetworkConfigListener implements NetworkConfigListener {
306 @Override
307 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500308 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
309 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400310 switch (event.type()) {
311 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400312 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400313 clearTxWorkers();
314 loadGlobalPrefixConfig();
315 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500316 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400317 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500318 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400319 }
320 }
321 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400322 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400323
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500324 private final InternalNetworkConfigListener networkConfigListener
325 = new InternalNetworkConfigListener();
326
327 // Handler for device updates
328 private class InternalDeviceListener implements DeviceListener {
329
330 @Override
331 public void event(DeviceEvent event) {
332 switch (event.type()) {
333 case DEVICE_ADDED:
334 case PORT_UPDATED:
335 case PORT_ADDED:
336 case DEVICE_UPDATED:
337 case DEVICE_AVAILABILITY_CHANGED:
338 clearTxWorkers();
339 setupTxWorkers();
340 log.trace("Processed device event {} on {}", event.type(), event.subject());
341 break;
342 default:
343 }
344 }
345 }
346
347 private final InternalDeviceListener internalDeviceListener =
348 new InternalDeviceListener();
349
350 // Processor for Solicited RA packets
351 private class InternalPacketProcessor implements PacketProcessor {
352
353 @Override
354 public void process(PacketContext context) {
355 if (context.isHandled()) {
356 return;
357 }
358
359 // Ensure packet is IPv6 Solicited RA
360 InboundPacket pkt = context.inPacket();
361 Ethernet ethernet = pkt.parsed();
362 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
363 return;
364 }
365 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
366 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
367 return;
368 }
369 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
370 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
371 return;
372 }
373
374 // Start solicited-RA handling thread
375 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
376 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
377 }
378 }
379
380 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400381
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400382 @Activate
383 protected void activate(ComponentContext context) {
384 // Basic application registrations.
385 appId = coreService.registerApplication(APP_NAME);
386 componentConfigService.registerProperties(getClass());
387
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500388 // Packet processor for handling Router Solicitations
389 processor = new InternalPacketProcessor();
390 packetService.addProcessor(processor, PacketProcessor.director(3));
391
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400392 // Setup global prefix loading components
393 networkConfigRegistry.addListener(networkConfigListener);
394 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
395 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400396
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500397 // Dynamic device updates handling
398 deviceService.addListener(internalDeviceListener);
399
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400400 // Setup pool and worker threads for existing interfaces
401 setupPoolAndTxWorkers();
402 }
403
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400404
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400405 @Modified
406 protected void modified(ComponentContext context) {
407 int newRaPoolSize, newRaThreadDelay;
408
409 // Loading configured properties.
410 if (context != null) {
411 Dictionary<?, ?> properties = context.getProperties();
412 try {
413 // Handle change in pool size
414 String s = get(properties, PROP_RA_THREADS_POOL);
415 newRaPoolSize = isNullOrEmpty(s) ?
416 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
417 if (newRaPoolSize != raPoolSize) {
418 raPoolSize = newRaPoolSize;
419 clearPoolAndTxWorkers();
420 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400421 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400422 }
423
424 // Handle change in thread delay
425 s = get(properties, PROP_RA_THREADS_DELAY);
426 newRaThreadDelay = isNullOrEmpty(s) ?
427 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
428 if (newRaThreadDelay != raThreadDelay) {
429 raThreadDelay = newRaThreadDelay;
430 clearTxWorkers();
431 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400432 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400433 }
434
435 // Handle M-flag changes
436 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400437 if (!isNullOrEmpty(s)) {
438 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
439 log.info("RA M-flag set {}", s);
440 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400441
442 // Handle O-flag changes
443 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400444 if (!isNullOrEmpty(s)) {
445 raFlagObitStatus = Boolean.parseBoolean(s.trim());
446 log.info("RA O-flag set {}", s);
447 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400448
449 // Handle prefix option configuration
450 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400451 if (!isNullOrEmpty(s)) {
452 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
453 String status = raOptionPrefixStatus ? "enabled" : "disabled";
454 log.info("RA prefix option {}", status);
455 }
456
457 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
458 if (!isNullOrEmpty(s)) {
459 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
460 clearTxWorkers();
461 setupTxWorkers();
462 String status = raOptionPrefixStatus ? "enabled" : "disabled";
463 log.info("RA global configuration file loading {}", status);
464 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400465
466 } catch (NumberFormatException e) {
467 log.warn("Component configuration had invalid value, aborting changes loading.", e);
468 }
469 }
470 }
471
472 @Deactivate
473 protected void deactivate() {
474 // Unregister resources.
475 componentConfigService.unregisterProperties(getClass(), false);
476 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500477 networkConfigRegistry.removeListener(networkConfigListener);
478 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
479 packetService.removeProcessor(processor);
480 deviceService.removeListener(internalDeviceListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400481
482 // Clear pool & threads
483 clearPoolAndTxWorkers();
484 }
485
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400486 // Worker thread for actually sending ICMPv6 RA packets.
487 private class RAWorkerThread implements Runnable {
488
489 ConnectPoint connectPoint;
490 List<InterfaceIpAddress> ipAddresses;
491 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500492 MacAddress solicitHostMac;
493 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400494
495 // Various fixed values in RA packet
496 public static final byte RA_HOP_LIMIT = (byte) 0xff;
497 public static final short RA_ROUTER_LIFETIME = (short) 1800;
498 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
499 public static final int RA_OPTION_MTU_VALUE = 1500;
500 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
501 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
502 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
503
504
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500505 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
506 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400507 this.connectPoint = connectPoint;
508 this.ipAddresses = ipAddresses;
509 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500510 solicitHostMac = macAddress;
511 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400512 }
513
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800514 @Override
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400515 public void run() {
516 // Router Advertisement header filling. Please refer RFC-2461.
517 RouterAdvertisement ra = new RouterAdvertisement();
518 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400519 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
520 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400521 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
522 ra.setReachableTime(0);
523 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
524
525 // Option : Source link-layer address.
526 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
527 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
528 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
529 .map(Interface::mac).findFirst();
530 if (!macAddress.isPresent()) {
531 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
532 return;
533 }
534 option.put(macAddress.get().toBytes());
535 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
536 Arrays.copyOfRange(option.array(), 0, option.position()));
537
538 // Option : MTU.
539 option.rewind();
540 option.putShort((short) 0);
541 option.putInt(RA_OPTION_MTU_VALUE);
542 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
543 Arrays.copyOfRange(option.array(), 0, option.position()));
544
545 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400546 if (raOptionPrefixStatus) {
547 ipAddresses.stream()
548 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
549 .forEach(i -> {
550 option.rewind();
551 option.put((byte) i.subnetAddress().prefixLength());
552 // Enable "onlink" option only.
553 option.put((byte) 0x80);
554 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
555 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
556 // Clear reserved fields
557 option.putInt(0x00000000);
558 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
559 i.subnetAddress().prefixLength()).toOctets());
560 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
561 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400562
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400563 });
564 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400565
566 // ICMPv6 header filling.
567 ICMP6 icmpv6 = new ICMP6();
568 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
569 icmpv6.setIcmpCode((byte) 0);
570 icmpv6.setPayload(ra);
571
572 // IPv6 header filling.
573 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
574 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500575 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400576 /* RA packet L2 source address created from port MAC address.
577 * Note : As per RFC-4861 RAs should be sent from link-local address.
578 */
579 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
580 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
581 ipv6.setHopLimit(RA_HOP_LIMIT);
582 ipv6.setTrafficClass((byte) 0xe0);
583 ipv6.setPayload(icmpv6);
584
585 // Ethernet header filling.
586 Ethernet ethernet = new Ethernet();
587
588 /* Ethernet IPv6 multicast address creation.
589 * Refer : RFC 2624 section 7.
590 */
591 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
592 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
593 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500594 // Provide unicast address for Solicit RA replays
595 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
596 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400597 ethernet.setSourceMACAddress(macAddress.get().toBytes());
598 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
599 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
600 ethernet.setPayload(ipv6);
601 ethernet.setPad(false);
602
603 // Flush out PACKET_OUT.
604 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
605 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
606 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
607 treatment, stream);
608 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500609 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400610 }
611 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500612
613 // Worker thread for processing IPv6 Solicited RA packets.
614 public class SolicitedRAWorkerThread implements Runnable {
615
616 private InboundPacket packet;
617
618 SolicitedRAWorkerThread(InboundPacket packet) {
619 this.packet = packet;
620 }
621
622 @Override
623 public void run() {
624
625 // TODO : Validate Router Solicitation
626
627 // Pause already running unsolicited RA threads in received connect point
628 ConnectPoint connectPoint = packet.receivedFrom();
629 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
630
631 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
632 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
633 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
634 */
635 log.trace("Processing Router Solicitations from {}", connectPoint);
636 try {
637 Ethernet ethernet = packet.parsed();
638 IPv6 ipv6 = (IPv6) ethernet.getPayload();
639 RAWorkerThread worker = new RAWorkerThread(connectPoint,
640 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
641 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
642 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
643 sraHandlerFuture.get();
644 } catch (Exception e) {
645 log.error("Failed to respond to router solicitation. {}", e);
646 } finally {
647 activateRouterAdvertisement(connectPoint, addresses);
648 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
649 }
650 }
651 }
652
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400653}