blob: 53d96b1e3dfb0f28e614a5157bf16bc680989e5b [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
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import com.google.common.collect.ImmutableMap;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040020import org.onlab.packet.EthType;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.ICMP6;
23import org.onlab.packet.IPv6;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040024import org.onlab.packet.Ip6Address;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050025import org.onlab.packet.IpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040026import org.onlab.packet.MacAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040027import org.onlab.packet.ndp.NeighborDiscoveryOptions;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050028import org.onlab.packet.ndp.RouterAdvertisement;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040029import org.onosproject.cfg.ComponentConfigService;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
Charles Chanbac954f92018-04-06 22:29:26 -070032import org.onosproject.mastership.MastershipEvent;
33import org.onosproject.mastership.MastershipListener;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040034import org.onosproject.mastership.MastershipService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050035import org.onosproject.net.ConnectPoint;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040036import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040037import org.onosproject.net.MastershipRole;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040038import org.onosproject.net.config.ConfigFactory;
39import org.onosproject.net.config.NetworkConfigEvent;
40import org.onosproject.net.config.NetworkConfigListener;
41import org.onosproject.net.config.NetworkConfigRegistry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050042import org.onosproject.net.config.basics.InterfaceConfig;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040043import org.onosproject.net.config.basics.SubjectFactories;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050044import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040046import org.onosproject.net.device.DeviceService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050047import org.onosproject.net.flow.DefaultTrafficTreatment;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.host.InterfaceIpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040050import org.onosproject.net.intf.Interface;
51import org.onosproject.net.intf.InterfaceEvent;
52import org.onosproject.net.intf.InterfaceListener;
53import org.onosproject.net.intf.InterfaceService;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040054import org.onosproject.net.packet.DefaultOutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050055import org.onosproject.net.packet.InboundPacket;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040056import org.onosproject.net.packet.OutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050057import org.onosproject.net.packet.PacketContext;
58import org.onosproject.net.packet.PacketProcessor;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040059import org.onosproject.net.packet.PacketService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040060import org.onosproject.ra.config.RouterAdvertisementDeviceConfig;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050061import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062import org.osgi.service.component.annotations.Activate;
63import org.osgi.service.component.annotations.Component;
64import org.osgi.service.component.annotations.Deactivate;
65import org.osgi.service.component.annotations.Modified;
66import org.osgi.service.component.annotations.Reference;
67import org.osgi.service.component.annotations.ReferenceCardinality;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040068import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040070
71import javax.annotation.concurrent.GuardedBy;
72import java.nio.ByteBuffer;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050073import java.util.AbstractMap;
74import java.util.Arrays;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040075import java.util.Dictionary;
76import java.util.LinkedHashMap;
77import java.util.List;
78import java.util.Map;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040079import java.util.Optional;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040080import java.util.Set;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050081import java.util.concurrent.CompletableFuture;
82import java.util.concurrent.Executors;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040083import java.util.concurrent.ScheduledExecutorService;
84import java.util.concurrent.ScheduledFuture;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040085import java.util.concurrent.TimeUnit;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040086import java.util.function.Function;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040087import java.util.stream.IntStream;
88
89import static com.google.common.base.Strings.isNullOrEmpty;
90import static org.onlab.util.Tools.get;
91import static org.onlab.util.Tools.groupedThreads;
Ray Milkey8e406512018-10-24 15:56:50 -070092import static org.onosproject.ra.OsgiPropertyConstants.RA_FLAG_MBIT_STATUS;
93import static org.onosproject.ra.OsgiPropertyConstants.RA_FLAG_MBIT_STATUS_DEFAULT;
94import static org.onosproject.ra.OsgiPropertyConstants.RA_FLAG_OBIT_STATUS;
95import static org.onosproject.ra.OsgiPropertyConstants.RA_FLAG_OBIT_STATUS_DEFAULT;
96import static org.onosproject.ra.OsgiPropertyConstants.RA_GLOBAL_PREFIX_CONF_STATUS;
97import static org.onosproject.ra.OsgiPropertyConstants.RA_GLOBAL_PREFIX_CONF_STATUS_DEFAULT;
98import static org.onosproject.ra.OsgiPropertyConstants.RA_OPTION_PREFIX_STATUS;
99import static org.onosproject.ra.OsgiPropertyConstants.RA_OPTION_PREFIX_STATUS_DEFAULT;
100import static org.onosproject.ra.OsgiPropertyConstants.RA_THREADS_DELAY;
101import static org.onosproject.ra.OsgiPropertyConstants.RA_THREADS_DELAY_DEFAULT;
102import static org.onosproject.ra.OsgiPropertyConstants.RA_THREADS_POOL;
103import static org.onosproject.ra.OsgiPropertyConstants.RA_THREADS_POOL_SIZE_DEFAULT;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400104
105/**
106 * Manages IPv6 Router Advertisements.
107 */
Ray Milkey8e406512018-10-24 15:56:50 -0700108@Component(
109 immediate = true,
110 service = RoutingAdvertisementService.class,
111 property = {
112 RA_THREADS_POOL + ":Integer=" + RA_THREADS_POOL_SIZE_DEFAULT,
113 RA_THREADS_DELAY + ":Integer=" + RA_THREADS_DELAY_DEFAULT,
114 RA_FLAG_MBIT_STATUS + ":Boolean=" + RA_FLAG_MBIT_STATUS_DEFAULT,
115 RA_FLAG_OBIT_STATUS + ":Boolean=" + RA_FLAG_OBIT_STATUS_DEFAULT,
116 RA_OPTION_PREFIX_STATUS + ":Boolean=" + RA_OPTION_PREFIX_STATUS_DEFAULT,
117 RA_GLOBAL_PREFIX_CONF_STATUS + ":Boolean=" + RA_GLOBAL_PREFIX_CONF_STATUS_DEFAULT
118 }
119)
Annce John0b4057a2017-11-21 01:46:41 -0500120public class RouterAdvertisementManager implements RoutingAdvertisementService {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400121
122 private final Logger log = LoggerFactory.getLogger(getClass());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400125 protected CoreService coreService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400128 PacketService packetService;
129
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400131 protected ComponentConfigService componentConfigService;
132
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400134 public InterfaceService interfaceService;
135
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400137 public MastershipService mastershipService;
138
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700139 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500140 protected NetworkConfigRegistry networkConfigRegistry;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500143 protected DeviceService deviceService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400144
Ray Milkey8e406512018-10-24 15:56:50 -0700145 /** Thread pool capacity. */
146 protected int raPoolSize = RA_THREADS_POOL_SIZE_DEFAULT;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400147
Ray Milkey8e406512018-10-24 15:56:50 -0700148 /** Thread delay in seconds. */
149 protected int raThreadDelay = RA_THREADS_DELAY_DEFAULT;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400150
Ray Milkey8e406512018-10-24 15:56:50 -0700151 /** Turn M-bit flag on/off. */
152 protected boolean raFlagMbitStatus = RA_FLAG_MBIT_STATUS_DEFAULT;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400153
Ray Milkey8e406512018-10-24 15:56:50 -0700154 /** Turn O-bit flag on/off. */
155 protected boolean raFlagObitStatus = RA_FLAG_OBIT_STATUS_DEFAULT;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400156
Ray Milkey8e406512018-10-24 15:56:50 -0700157 /** Prefix option support needed or not. */
158 protected boolean raOptionPrefixStatus = RA_OPTION_PREFIX_STATUS_DEFAULT;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400159
Ray Milkey8e406512018-10-24 15:56:50 -0700160 /** Global prefix configuration support on/off. */
161 protected boolean raGlobalPrefixConfStatus = RA_GLOBAL_PREFIX_CONF_STATUS_DEFAULT;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400162
163 @GuardedBy(value = "this")
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500164 private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
165 new LinkedHashMap<>();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400166
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800167 // TODO: should consider using concurrent variants
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400168 @GuardedBy(value = "this")
169 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
170
Annce John0b4057a2017-11-21 01:46:41 -0500171 @Override
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800172 public synchronized ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
Annce John0b4057a2017-11-21 01:46:41 -0500173 return ImmutableMap.copyOf(globalPrefixes);
174 }
175
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800176 @SuppressWarnings("GuardedBy")
177 @GuardedBy(value = "this")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400178 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
179 i -> {
180 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
Ray Milkey8e406512018-10-24 15:56:50 -0700181 if (raGlobalPrefixConfStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400182 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
183 globalPrefixes.get(i.connectPoint().deviceId()));
184 } else {
185 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
186 }
187 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500188 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400189
190 private ScheduledExecutorService executors = null;
191
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400192 private static final String APP_NAME = "org.onosproject.routeradvertisement";
193 private ApplicationId appId;
194
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400195 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
196 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
197 SubjectFactories.DEVICE_SUBJECT_FACTORY,
198 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
199 @Override
200 public RouterAdvertisementDeviceConfig createConfig() {
201
202 return new RouterAdvertisementDeviceConfig();
203 }
204 };
205
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400206 // Listener for handling dynamic interface modifications.
207 private class InternalInterfaceListener implements InterfaceListener {
208 @Override
209 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400210 switch (event.type()) {
211 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500212 case INTERFACE_UPDATED:
213 clearTxWorkers();
214 loadGlobalPrefixConfig();
215 setupTxWorkers();
216 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400217 break;
218 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500219 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400220 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
221 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500222 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400223 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400224 break;
225 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400226 }
227 }
228 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400229
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400230 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
231
232 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400233 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
234 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500235 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400236 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
237 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500238 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400239 }
240
241 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500242 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400243 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500244 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
245 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400246 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500247 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400248 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500249 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400250 }
251
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400252 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400253 executors = Executors.newScheduledThreadPool(raPoolSize,
254 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400255 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400256
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400257 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400258 executors.shutdown();
259 }
260
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400261 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400262 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400263 interfaceService.getInterfaces()
264 .stream()
265 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
266 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400267 .map(prefixGenerator::apply)
268 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400269 .stream()
270 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
271 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400272 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400273 );
274 }
275
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400276 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400277 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500278 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400279 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400280 }
281
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400282 private synchronized void setupPoolAndTxWorkers() {
283 setupThreadPool();
284 setupTxWorkers();
285 }
286
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400287 private synchronized void clearPoolAndTxWorkers() {
288 clearTxWorkers();
289 clearThreadPool();
290 }
291
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800292 @SuppressWarnings("GuardedBy")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400293 // Loading global prefixes for devices from network configuration
294 private synchronized void loadGlobalPrefixConfig() {
295 globalPrefixes.clear();
296 Set<DeviceId> deviceSubjects =
297 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
298 deviceSubjects.forEach(subject -> {
299 RouterAdvertisementDeviceConfig config =
300 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
301 if (config != null) {
302 List<InterfaceIpAddress> ips = config.prefixes();
303 globalPrefixes.put(subject, ips);
304 }
305 });
306 }
307
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500308 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400309 private class InternalNetworkConfigListener implements NetworkConfigListener {
310 @Override
311 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500312 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
313 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400314 switch (event.type()) {
315 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400316 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400317 clearTxWorkers();
318 loadGlobalPrefixConfig();
319 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500320 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400321 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500322 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400323 }
324 }
325 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400326 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400327
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500328 private final InternalNetworkConfigListener networkConfigListener
329 = new InternalNetworkConfigListener();
330
331 // Handler for device updates
332 private class InternalDeviceListener implements DeviceListener {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500333 @Override
334 public void event(DeviceEvent event) {
335 switch (event.type()) {
336 case DEVICE_ADDED:
337 case PORT_UPDATED:
338 case PORT_ADDED:
339 case DEVICE_UPDATED:
340 case DEVICE_AVAILABILITY_CHANGED:
341 clearTxWorkers();
342 setupTxWorkers();
343 log.trace("Processed device event {} on {}", event.type(), event.subject());
344 break;
345 default:
346 }
347 }
348 }
349
Charles Chanbac954f92018-04-06 22:29:26 -0700350 private class InternalMastershipListener implements MastershipListener {
351 @Override
352 public void event(MastershipEvent event) {
353 switch (event.type()) {
354 case MASTER_CHANGED:
355 clearTxWorkers();
356 setupTxWorkers();
357 log.trace("Processed mastership event {} on {}", event.type(), event.subject());
358 break;
359 case BACKUPS_CHANGED:
360 case SUSPENDED:
361 default:
362 break;
363 }
364 }
365 }
366
367 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
368 private final InternalMastershipListener internalMastershipListener = new InternalMastershipListener();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500369
370 // Processor for Solicited RA packets
371 private class InternalPacketProcessor implements PacketProcessor {
372
373 @Override
374 public void process(PacketContext context) {
375 if (context.isHandled()) {
376 return;
377 }
378
379 // Ensure packet is IPv6 Solicited RA
380 InboundPacket pkt = context.inPacket();
381 Ethernet ethernet = pkt.parsed();
382 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
383 return;
384 }
385 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
386 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
387 return;
388 }
389 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
390 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
391 return;
392 }
393
394 // Start solicited-RA handling thread
395 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
396 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
397 }
398 }
399
400 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400401
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400402 @Activate
403 protected void activate(ComponentContext context) {
404 // Basic application registrations.
405 appId = coreService.registerApplication(APP_NAME);
406 componentConfigService.registerProperties(getClass());
407
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500408 // Packet processor for handling Router Solicitations
409 processor = new InternalPacketProcessor();
410 packetService.addProcessor(processor, PacketProcessor.director(3));
411
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400412 // Setup global prefix loading components
413 networkConfigRegistry.addListener(networkConfigListener);
414 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
415 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400416
Charles Chanbac954f92018-04-06 22:29:26 -0700417 // Register device and mastership event listener
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500418 deviceService.addListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700419 mastershipService.addListener(internalMastershipListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500420
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400421 // Setup pool and worker threads for existing interfaces
422 setupPoolAndTxWorkers();
423 }
424
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400425
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400426 @Modified
427 protected void modified(ComponentContext context) {
428 int newRaPoolSize, newRaThreadDelay;
429
430 // Loading configured properties.
431 if (context != null) {
432 Dictionary<?, ?> properties = context.getProperties();
433 try {
434 // Handle change in pool size
Ray Milkey8e406512018-10-24 15:56:50 -0700435 String s = get(properties, RA_THREADS_POOL);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400436 newRaPoolSize = isNullOrEmpty(s) ?
Ray Milkey8e406512018-10-24 15:56:50 -0700437 RA_THREADS_POOL_SIZE_DEFAULT : Integer.parseInt(s.trim());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400438 if (newRaPoolSize != raPoolSize) {
439 raPoolSize = newRaPoolSize;
440 clearPoolAndTxWorkers();
441 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400442 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400443 }
444
445 // Handle change in thread delay
Ray Milkey8e406512018-10-24 15:56:50 -0700446 s = get(properties, RA_THREADS_DELAY);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400447 newRaThreadDelay = isNullOrEmpty(s) ?
Ray Milkey8e406512018-10-24 15:56:50 -0700448 RA_THREADS_DELAY_DEFAULT : Integer.parseInt(s.trim());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400449 if (newRaThreadDelay != raThreadDelay) {
450 raThreadDelay = newRaThreadDelay;
451 clearTxWorkers();
452 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400453 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400454 }
455
456 // Handle M-flag changes
Ray Milkey8e406512018-10-24 15:56:50 -0700457 s = get(properties, RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400458 if (!isNullOrEmpty(s)) {
459 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
460 log.info("RA M-flag set {}", s);
461 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400462
463 // Handle O-flag changes
Ray Milkey8e406512018-10-24 15:56:50 -0700464 s = get(properties, RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400465 if (!isNullOrEmpty(s)) {
466 raFlagObitStatus = Boolean.parseBoolean(s.trim());
467 log.info("RA O-flag set {}", s);
468 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400469
470 // Handle prefix option configuration
Ray Milkey8e406512018-10-24 15:56:50 -0700471 s = get(properties, RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400472 if (!isNullOrEmpty(s)) {
473 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
474 String status = raOptionPrefixStatus ? "enabled" : "disabled";
475 log.info("RA prefix option {}", status);
476 }
477
Ray Milkey8e406512018-10-24 15:56:50 -0700478 s = get(properties, RA_GLOBAL_PREFIX_CONF_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400479 if (!isNullOrEmpty(s)) {
Ray Milkey8e406512018-10-24 15:56:50 -0700480 raGlobalPrefixConfStatus = Boolean.parseBoolean(s.trim());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400481 clearTxWorkers();
482 setupTxWorkers();
483 String status = raOptionPrefixStatus ? "enabled" : "disabled";
484 log.info("RA global configuration file loading {}", status);
485 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400486
487 } catch (NumberFormatException e) {
488 log.warn("Component configuration had invalid value, aborting changes loading.", e);
489 }
490 }
491 }
492
493 @Deactivate
494 protected void deactivate() {
495 // Unregister resources.
496 componentConfigService.unregisterProperties(getClass(), false);
497 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500498 networkConfigRegistry.removeListener(networkConfigListener);
499 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
500 packetService.removeProcessor(processor);
501 deviceService.removeListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700502 mastershipService.removeListener(internalMastershipListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400503
504 // Clear pool & threads
505 clearPoolAndTxWorkers();
506 }
507
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400508 // Worker thread for actually sending ICMPv6 RA packets.
509 private class RAWorkerThread implements Runnable {
510
511 ConnectPoint connectPoint;
512 List<InterfaceIpAddress> ipAddresses;
513 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500514 MacAddress solicitHostMac;
515 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400516
517 // Various fixed values in RA packet
518 public static final byte RA_HOP_LIMIT = (byte) 0xff;
519 public static final short RA_ROUTER_LIFETIME = (short) 1800;
520 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
521 public static final int RA_OPTION_MTU_VALUE = 1500;
522 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
523 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
524 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
525
526
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500527 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
528 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400529 this.connectPoint = connectPoint;
530 this.ipAddresses = ipAddresses;
531 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500532 solicitHostMac = macAddress;
533 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400534 }
535
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800536 @Override
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400537 public void run() {
538 // Router Advertisement header filling. Please refer RFC-2461.
539 RouterAdvertisement ra = new RouterAdvertisement();
540 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400541 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
542 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400543 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
544 ra.setReachableTime(0);
545 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
546
547 // Option : Source link-layer address.
548 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
549 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
550 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
551 .map(Interface::mac).findFirst();
552 if (!macAddress.isPresent()) {
553 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
554 return;
555 }
556 option.put(macAddress.get().toBytes());
557 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
558 Arrays.copyOfRange(option.array(), 0, option.position()));
559
560 // Option : MTU.
561 option.rewind();
562 option.putShort((short) 0);
563 option.putInt(RA_OPTION_MTU_VALUE);
564 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
565 Arrays.copyOfRange(option.array(), 0, option.position()));
566
567 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400568 if (raOptionPrefixStatus) {
569 ipAddresses.stream()
570 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
571 .forEach(i -> {
572 option.rewind();
573 option.put((byte) i.subnetAddress().prefixLength());
574 // Enable "onlink" option only.
575 option.put((byte) 0x80);
576 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
577 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
578 // Clear reserved fields
579 option.putInt(0x00000000);
580 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
581 i.subnetAddress().prefixLength()).toOctets());
582 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
583 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400584
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400585 });
586 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400587
588 // ICMPv6 header filling.
589 ICMP6 icmpv6 = new ICMP6();
590 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
591 icmpv6.setIcmpCode((byte) 0);
592 icmpv6.setPayload(ra);
593
594 // IPv6 header filling.
595 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
596 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500597 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400598 /* RA packet L2 source address created from port MAC address.
599 * Note : As per RFC-4861 RAs should be sent from link-local address.
600 */
601 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
602 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
603 ipv6.setHopLimit(RA_HOP_LIMIT);
604 ipv6.setTrafficClass((byte) 0xe0);
605 ipv6.setPayload(icmpv6);
606
607 // Ethernet header filling.
608 Ethernet ethernet = new Ethernet();
609
610 /* Ethernet IPv6 multicast address creation.
611 * Refer : RFC 2624 section 7.
612 */
613 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
614 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
615 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500616 // Provide unicast address for Solicit RA replays
617 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
618 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400619 ethernet.setSourceMACAddress(macAddress.get().toBytes());
620 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
621 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
622 ethernet.setPayload(ipv6);
623 ethernet.setPad(false);
624
625 // Flush out PACKET_OUT.
626 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
627 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
628 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
629 treatment, stream);
630 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500631 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400632 }
633 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500634
635 // Worker thread for processing IPv6 Solicited RA packets.
636 public class SolicitedRAWorkerThread implements Runnable {
637
638 private InboundPacket packet;
639
640 SolicitedRAWorkerThread(InboundPacket packet) {
641 this.packet = packet;
642 }
643
644 @Override
645 public void run() {
646
647 // TODO : Validate Router Solicitation
648
649 // Pause already running unsolicited RA threads in received connect point
650 ConnectPoint connectPoint = packet.receivedFrom();
651 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
652
653 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
654 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
655 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
656 */
657 log.trace("Processing Router Solicitations from {}", connectPoint);
658 try {
659 Ethernet ethernet = packet.parsed();
660 IPv6 ipv6 = (IPv6) ethernet.getPayload();
661 RAWorkerThread worker = new RAWorkerThread(connectPoint,
662 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
663 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
664 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
665 sraHandlerFuture.get();
666 } catch (Exception e) {
667 log.error("Failed to respond to router solicitation. {}", e);
668 } finally {
669 activateRouterAdvertisement(connectPoint, addresses);
670 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
671 }
672 }
673 }
674
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400675}