blob: 8da5065dfcc4725662c851aa61be2103eda7ee42 [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;
92
93/**
94 * Manages IPv6 Router Advertisements.
95 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096@Component(immediate = true, service = RoutingAdvertisementService.class)
Annce John0b4057a2017-11-21 01:46:41 -050097public class RouterAdvertisementManager implements RoutingAdvertisementService {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040098
99 private final Logger log = LoggerFactory.getLogger(getClass());
100 private static final String PROP_RA_THREADS_POOL = "raPoolSize";
101 private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
102 private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
103 private static final int DEFAULT_RA_THREADS_DELAY = 5;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400104 private static final String PROP_RA_FLAG_MBIT_STATUS = "raFlagMbitStatus";
105 private static final boolean DEFAULT_RA_FLAG_MBIT_STATUS = false;
106 private static final String PROP_RA_FLAG_OBIT_STATUS = "raFlagObitStatus";
107 private static final boolean DEFAULT_RA_FLAG_OBIT_STATUS = false;
108 private static final String PROP_RA_OPTION_PREFIX_STATUS = "raOptionPrefixStatus";
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400109 private static final boolean DEFAULT_RA_OPTION_PREFIX_STATUS = false;
110 private static final String PROP_RA_GLOBAL_PREFIX_CONF_STATUS = "raGlobalPrefixConfStatus";
111 private static final boolean DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS = true;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400112
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400114 protected CoreService coreService;
115
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400117 PacketService packetService;
118
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700119 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400120 protected ComponentConfigService componentConfigService;
121
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400123 public InterfaceService interfaceService;
124
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700125 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400126 public MastershipService mastershipService;
127
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700128 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500129 protected NetworkConfigRegistry networkConfigRegistry;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400130
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700131 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500132 protected DeviceService deviceService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400133
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 //@Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
135 // label = "Thread pool capacity")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400136 protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
137
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700138 //@Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
139 // label = "Thread delay in seconds")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400140 protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
141
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700142 //@Property(name = PROP_RA_FLAG_MBIT_STATUS, boolValue = DEFAULT_RA_FLAG_MBIT_STATUS,
143 // label = "Turn M-bit flag on/off")
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400144 protected boolean raFlagMbitStatus = DEFAULT_RA_FLAG_MBIT_STATUS;
145
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700146 //@Property(name = PROP_RA_FLAG_OBIT_STATUS, boolValue = DEFAULT_RA_FLAG_OBIT_STATUS,
147 // label = "Turn O-bit flag on/off")
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400148 protected boolean raFlagObitStatus = DEFAULT_RA_FLAG_OBIT_STATUS;
149
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700150 //@Property(name = PROP_RA_OPTION_PREFIX_STATUS, boolValue = DEFAULT_RA_OPTION_PREFIX_STATUS,
151 // label = "Prefix option support needed or not")
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400152 protected boolean raOptionPrefixStatus = DEFAULT_RA_OPTION_PREFIX_STATUS;
153
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700154 //@Property(name = PROP_RA_GLOBAL_PREFIX_CONF_STATUS, boolValue = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS,
155 // label = "Global prefix configuration support on/off")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400156 protected boolean raGlobalConfigStatus = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400157
158 @GuardedBy(value = "this")
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500159 private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
160 new LinkedHashMap<>();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400161
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800162 // TODO: should consider using concurrent variants
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
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800167 public synchronized ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
Annce John0b4057a2017-11-21 01:46:41 -0500168 return ImmutableMap.copyOf(globalPrefixes);
169 }
170
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800171 @SuppressWarnings("GuardedBy")
172 @GuardedBy(value = "this")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400173 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
174 i -> {
175 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
176 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
177 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
178 globalPrefixes.get(i.connectPoint().deviceId()));
179 } else {
180 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
181 }
182 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500183 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400184
185 private ScheduledExecutorService executors = null;
186
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400187 private static final String APP_NAME = "org.onosproject.routeradvertisement";
188 private ApplicationId appId;
189
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400190 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
191 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
192 SubjectFactories.DEVICE_SUBJECT_FACTORY,
193 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
194 @Override
195 public RouterAdvertisementDeviceConfig createConfig() {
196
197 return new RouterAdvertisementDeviceConfig();
198 }
199 };
200
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400201 // Listener for handling dynamic interface modifications.
202 private class InternalInterfaceListener implements InterfaceListener {
203 @Override
204 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400205 switch (event.type()) {
206 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500207 case INTERFACE_UPDATED:
208 clearTxWorkers();
209 loadGlobalPrefixConfig();
210 setupTxWorkers();
211 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400212 break;
213 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500214 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400215 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
216 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500217 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400218 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400219 break;
220 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400221 }
222 }
223 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400224
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400225 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
226
227 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400228 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
229 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500230 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400231 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
232 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500233 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400234 }
235
236 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500237 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400238 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500239 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
240 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400241 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500242 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400243 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500244 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400245 }
246
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400247 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400248 executors = Executors.newScheduledThreadPool(raPoolSize,
249 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400250 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400251
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400252 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400253 executors.shutdown();
254 }
255
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400256 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400257 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400258 interfaceService.getInterfaces()
259 .stream()
260 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
261 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400262 .map(prefixGenerator::apply)
263 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400264 .stream()
265 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
266 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400267 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400268 );
269 }
270
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400271 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400272 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500273 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400274 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400275 }
276
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400277 private synchronized void setupPoolAndTxWorkers() {
278 setupThreadPool();
279 setupTxWorkers();
280 }
281
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400282 private synchronized void clearPoolAndTxWorkers() {
283 clearTxWorkers();
284 clearThreadPool();
285 }
286
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800287 @SuppressWarnings("GuardedBy")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400288 // Loading global prefixes for devices from network configuration
289 private synchronized void loadGlobalPrefixConfig() {
290 globalPrefixes.clear();
291 Set<DeviceId> deviceSubjects =
292 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
293 deviceSubjects.forEach(subject -> {
294 RouterAdvertisementDeviceConfig config =
295 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
296 if (config != null) {
297 List<InterfaceIpAddress> ips = config.prefixes();
298 globalPrefixes.put(subject, ips);
299 }
300 });
301 }
302
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500303 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400304 private class InternalNetworkConfigListener implements NetworkConfigListener {
305 @Override
306 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500307 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
308 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400309 switch (event.type()) {
310 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400311 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400312 clearTxWorkers();
313 loadGlobalPrefixConfig();
314 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500315 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400316 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500317 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400318 }
319 }
320 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400321 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400322
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500323 private final InternalNetworkConfigListener networkConfigListener
324 = new InternalNetworkConfigListener();
325
326 // Handler for device updates
327 private class InternalDeviceListener implements DeviceListener {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500328 @Override
329 public void event(DeviceEvent event) {
330 switch (event.type()) {
331 case DEVICE_ADDED:
332 case PORT_UPDATED:
333 case PORT_ADDED:
334 case DEVICE_UPDATED:
335 case DEVICE_AVAILABILITY_CHANGED:
336 clearTxWorkers();
337 setupTxWorkers();
338 log.trace("Processed device event {} on {}", event.type(), event.subject());
339 break;
340 default:
341 }
342 }
343 }
344
Charles Chanbac954f92018-04-06 22:29:26 -0700345 private class InternalMastershipListener implements MastershipListener {
346 @Override
347 public void event(MastershipEvent event) {
348 switch (event.type()) {
349 case MASTER_CHANGED:
350 clearTxWorkers();
351 setupTxWorkers();
352 log.trace("Processed mastership event {} on {}", event.type(), event.subject());
353 break;
354 case BACKUPS_CHANGED:
355 case SUSPENDED:
356 default:
357 break;
358 }
359 }
360 }
361
362 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
363 private final InternalMastershipListener internalMastershipListener = new InternalMastershipListener();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500364
365 // Processor for Solicited RA packets
366 private class InternalPacketProcessor implements PacketProcessor {
367
368 @Override
369 public void process(PacketContext context) {
370 if (context.isHandled()) {
371 return;
372 }
373
374 // Ensure packet is IPv6 Solicited RA
375 InboundPacket pkt = context.inPacket();
376 Ethernet ethernet = pkt.parsed();
377 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
378 return;
379 }
380 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
381 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
382 return;
383 }
384 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
385 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
386 return;
387 }
388
389 // Start solicited-RA handling thread
390 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
391 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
392 }
393 }
394
395 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400396
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400397 @Activate
398 protected void activate(ComponentContext context) {
399 // Basic application registrations.
400 appId = coreService.registerApplication(APP_NAME);
401 componentConfigService.registerProperties(getClass());
402
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500403 // Packet processor for handling Router Solicitations
404 processor = new InternalPacketProcessor();
405 packetService.addProcessor(processor, PacketProcessor.director(3));
406
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400407 // Setup global prefix loading components
408 networkConfigRegistry.addListener(networkConfigListener);
409 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
410 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400411
Charles Chanbac954f92018-04-06 22:29:26 -0700412 // Register device and mastership event listener
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500413 deviceService.addListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700414 mastershipService.addListener(internalMastershipListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500415
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400416 // Setup pool and worker threads for existing interfaces
417 setupPoolAndTxWorkers();
418 }
419
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400420
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400421 @Modified
422 protected void modified(ComponentContext context) {
423 int newRaPoolSize, newRaThreadDelay;
424
425 // Loading configured properties.
426 if (context != null) {
427 Dictionary<?, ?> properties = context.getProperties();
428 try {
429 // Handle change in pool size
430 String s = get(properties, PROP_RA_THREADS_POOL);
431 newRaPoolSize = isNullOrEmpty(s) ?
432 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
433 if (newRaPoolSize != raPoolSize) {
434 raPoolSize = newRaPoolSize;
435 clearPoolAndTxWorkers();
436 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400437 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400438 }
439
440 // Handle change in thread delay
441 s = get(properties, PROP_RA_THREADS_DELAY);
442 newRaThreadDelay = isNullOrEmpty(s) ?
443 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
444 if (newRaThreadDelay != raThreadDelay) {
445 raThreadDelay = newRaThreadDelay;
446 clearTxWorkers();
447 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400448 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400449 }
450
451 // Handle M-flag changes
452 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400453 if (!isNullOrEmpty(s)) {
454 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
455 log.info("RA M-flag set {}", s);
456 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400457
458 // Handle O-flag changes
459 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400460 if (!isNullOrEmpty(s)) {
461 raFlagObitStatus = Boolean.parseBoolean(s.trim());
462 log.info("RA O-flag set {}", s);
463 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400464
465 // Handle prefix option configuration
466 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400467 if (!isNullOrEmpty(s)) {
468 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
469 String status = raOptionPrefixStatus ? "enabled" : "disabled";
470 log.info("RA prefix option {}", status);
471 }
472
473 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
474 if (!isNullOrEmpty(s)) {
475 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
476 clearTxWorkers();
477 setupTxWorkers();
478 String status = raOptionPrefixStatus ? "enabled" : "disabled";
479 log.info("RA global configuration file loading {}", status);
480 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400481
482 } catch (NumberFormatException e) {
483 log.warn("Component configuration had invalid value, aborting changes loading.", e);
484 }
485 }
486 }
487
488 @Deactivate
489 protected void deactivate() {
490 // Unregister resources.
491 componentConfigService.unregisterProperties(getClass(), false);
492 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500493 networkConfigRegistry.removeListener(networkConfigListener);
494 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
495 packetService.removeProcessor(processor);
496 deviceService.removeListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700497 mastershipService.removeListener(internalMastershipListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400498
499 // Clear pool & threads
500 clearPoolAndTxWorkers();
501 }
502
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400503 // Worker thread for actually sending ICMPv6 RA packets.
504 private class RAWorkerThread implements Runnable {
505
506 ConnectPoint connectPoint;
507 List<InterfaceIpAddress> ipAddresses;
508 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500509 MacAddress solicitHostMac;
510 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400511
512 // Various fixed values in RA packet
513 public static final byte RA_HOP_LIMIT = (byte) 0xff;
514 public static final short RA_ROUTER_LIFETIME = (short) 1800;
515 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
516 public static final int RA_OPTION_MTU_VALUE = 1500;
517 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
518 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
519 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
520
521
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500522 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
523 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400524 this.connectPoint = connectPoint;
525 this.ipAddresses = ipAddresses;
526 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500527 solicitHostMac = macAddress;
528 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400529 }
530
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800531 @Override
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400532 public void run() {
533 // Router Advertisement header filling. Please refer RFC-2461.
534 RouterAdvertisement ra = new RouterAdvertisement();
535 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400536 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
537 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400538 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
539 ra.setReachableTime(0);
540 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
541
542 // Option : Source link-layer address.
543 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
544 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
545 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
546 .map(Interface::mac).findFirst();
547 if (!macAddress.isPresent()) {
548 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
549 return;
550 }
551 option.put(macAddress.get().toBytes());
552 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
553 Arrays.copyOfRange(option.array(), 0, option.position()));
554
555 // Option : MTU.
556 option.rewind();
557 option.putShort((short) 0);
558 option.putInt(RA_OPTION_MTU_VALUE);
559 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
560 Arrays.copyOfRange(option.array(), 0, option.position()));
561
562 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400563 if (raOptionPrefixStatus) {
564 ipAddresses.stream()
565 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
566 .forEach(i -> {
567 option.rewind();
568 option.put((byte) i.subnetAddress().prefixLength());
569 // Enable "onlink" option only.
570 option.put((byte) 0x80);
571 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
572 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
573 // Clear reserved fields
574 option.putInt(0x00000000);
575 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
576 i.subnetAddress().prefixLength()).toOctets());
577 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
578 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400579
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400580 });
581 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400582
583 // ICMPv6 header filling.
584 ICMP6 icmpv6 = new ICMP6();
585 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
586 icmpv6.setIcmpCode((byte) 0);
587 icmpv6.setPayload(ra);
588
589 // IPv6 header filling.
590 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
591 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500592 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400593 /* RA packet L2 source address created from port MAC address.
594 * Note : As per RFC-4861 RAs should be sent from link-local address.
595 */
596 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
597 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
598 ipv6.setHopLimit(RA_HOP_LIMIT);
599 ipv6.setTrafficClass((byte) 0xe0);
600 ipv6.setPayload(icmpv6);
601
602 // Ethernet header filling.
603 Ethernet ethernet = new Ethernet();
604
605 /* Ethernet IPv6 multicast address creation.
606 * Refer : RFC 2624 section 7.
607 */
608 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
609 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
610 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500611 // Provide unicast address for Solicit RA replays
612 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
613 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400614 ethernet.setSourceMACAddress(macAddress.get().toBytes());
615 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
616 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
617 ethernet.setPayload(ipv6);
618 ethernet.setPad(false);
619
620 // Flush out PACKET_OUT.
621 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
622 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
623 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
624 treatment, stream);
625 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500626 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400627 }
628 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500629
630 // Worker thread for processing IPv6 Solicited RA packets.
631 public class SolicitedRAWorkerThread implements Runnable {
632
633 private InboundPacket packet;
634
635 SolicitedRAWorkerThread(InboundPacket packet) {
636 this.packet = packet;
637 }
638
639 @Override
640 public void run() {
641
642 // TODO : Validate Router Solicitation
643
644 // Pause already running unsolicited RA threads in received connect point
645 ConnectPoint connectPoint = packet.receivedFrom();
646 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
647
648 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
649 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
650 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
651 */
652 log.trace("Processing Router Solicitations from {}", connectPoint);
653 try {
654 Ethernet ethernet = packet.parsed();
655 IPv6 ipv6 = (IPv6) ethernet.getPayload();
656 RAWorkerThread worker = new RAWorkerThread(connectPoint,
657 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
658 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
659 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
660 sraHandlerFuture.get();
661 } catch (Exception e) {
662 log.error("Failed to respond to router solicitation. {}", e);
663 } finally {
664 activateRouterAdvertisement(connectPoint, addresses);
665 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
666 }
667 }
668 }
669
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400670}