blob: f568e90922edaa9eb7ec4b5077dd2e74317f346f [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;
Charles Chanbac954f92018-04-06 22:29:26 -070039import org.onosproject.mastership.MastershipEvent;
40import org.onosproject.mastership.MastershipListener;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040041import org.onosproject.mastership.MastershipService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050042import org.onosproject.net.ConnectPoint;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040043import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040044import org.onosproject.net.MastershipRole;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040045import org.onosproject.net.config.ConfigFactory;
46import org.onosproject.net.config.NetworkConfigEvent;
47import org.onosproject.net.config.NetworkConfigListener;
48import org.onosproject.net.config.NetworkConfigRegistry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050049import org.onosproject.net.config.basics.InterfaceConfig;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040050import org.onosproject.net.config.basics.SubjectFactories;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050051import org.onosproject.net.device.DeviceEvent;
52import org.onosproject.net.device.DeviceListener;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040053import org.onosproject.net.device.DeviceService;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050054import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.TrafficTreatment;
56import org.onosproject.net.host.InterfaceIpAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040057import org.onosproject.net.intf.Interface;
58import org.onosproject.net.intf.InterfaceEvent;
59import org.onosproject.net.intf.InterfaceListener;
60import org.onosproject.net.intf.InterfaceService;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040061import org.onosproject.net.packet.DefaultOutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050062import org.onosproject.net.packet.InboundPacket;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040063import org.onosproject.net.packet.OutboundPacket;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050064import org.onosproject.net.packet.PacketContext;
65import org.onosproject.net.packet.PacketProcessor;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040066import org.onosproject.net.packet.PacketService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040067import org.onosproject.ra.config.RouterAdvertisementDeviceConfig;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050068import org.osgi.service.component.ComponentContext;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040069import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040071
72import javax.annotation.concurrent.GuardedBy;
73import java.nio.ByteBuffer;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050074import java.util.AbstractMap;
75import java.util.Arrays;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040076import java.util.Dictionary;
77import java.util.LinkedHashMap;
78import java.util.List;
79import java.util.Map;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040080import java.util.Optional;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040081import java.util.Set;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -050082import java.util.concurrent.CompletableFuture;
83import java.util.concurrent.Executors;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040084import java.util.concurrent.ScheduledExecutorService;
85import java.util.concurrent.ScheduledFuture;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040086import java.util.concurrent.TimeUnit;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040087import java.util.function.Function;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040088import java.util.stream.IntStream;
89
90import static com.google.common.base.Strings.isNullOrEmpty;
91import static org.onlab.util.Tools.get;
92import static org.onlab.util.Tools.groupedThreads;
Annce John0b4057a2017-11-21 01:46:41 -050093import com.google.common.collect.ImmutableMap;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040094
95/**
96 * Manages IPv6 Router Advertisements.
97 */
Annce John0b4057a2017-11-21 01:46:41 -050098@Service
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040099@Component(immediate = true)
Annce John0b4057a2017-11-21 01:46:41 -0500100public class RouterAdvertisementManager implements RoutingAdvertisementService {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400101
102 private final Logger log = LoggerFactory.getLogger(getClass());
103 private static final String PROP_RA_THREADS_POOL = "raPoolSize";
104 private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
105 private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
106 private static final int DEFAULT_RA_THREADS_DELAY = 5;
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400107 private static final String PROP_RA_FLAG_MBIT_STATUS = "raFlagMbitStatus";
108 private static final boolean DEFAULT_RA_FLAG_MBIT_STATUS = false;
109 private static final String PROP_RA_FLAG_OBIT_STATUS = "raFlagObitStatus";
110 private static final boolean DEFAULT_RA_FLAG_OBIT_STATUS = false;
111 private static final String PROP_RA_OPTION_PREFIX_STATUS = "raOptionPrefixStatus";
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400112 private static final boolean DEFAULT_RA_OPTION_PREFIX_STATUS = false;
113 private static final String PROP_RA_GLOBAL_PREFIX_CONF_STATUS = "raGlobalPrefixConfStatus";
114 private static final boolean DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS = true;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 PacketService packetService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected ComponentConfigService componentConfigService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 public InterfaceService interfaceService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 public MastershipService mastershipService;
130
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500132 protected NetworkConfigRegistry networkConfigRegistry;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400133
134 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500135 protected DeviceService deviceService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400136
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400137 @Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400138 label = "Thread pool capacity")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400139 protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
140
141 @Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400142 label = "Thread delay in seconds")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400143 protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
144
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400145 @Property(name = PROP_RA_FLAG_MBIT_STATUS, boolValue = DEFAULT_RA_FLAG_MBIT_STATUS,
146 label = "Turn M-bit flag on/off")
147 protected boolean raFlagMbitStatus = DEFAULT_RA_FLAG_MBIT_STATUS;
148
149 @Property(name = PROP_RA_FLAG_OBIT_STATUS, boolValue = DEFAULT_RA_FLAG_OBIT_STATUS,
150 label = "Turn O-bit flag on/off")
151 protected boolean raFlagObitStatus = DEFAULT_RA_FLAG_OBIT_STATUS;
152
153 @Property(name = PROP_RA_OPTION_PREFIX_STATUS, boolValue = DEFAULT_RA_OPTION_PREFIX_STATUS,
154 label = "Prefix option support needed or not")
155 protected boolean raOptionPrefixStatus = DEFAULT_RA_OPTION_PREFIX_STATUS;
156
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400157 @Property(name = PROP_RA_GLOBAL_PREFIX_CONF_STATUS, boolValue = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS,
158 label = "Global prefix configuration support on/off")
159 protected boolean raGlobalConfigStatus = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400160
161 @GuardedBy(value = "this")
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500162 private final Map<ConnectPoint, Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>>> transmitters =
163 new LinkedHashMap<>();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400164
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800165 // TODO: should consider using concurrent variants
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400166 @GuardedBy(value = "this")
167 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
168
Annce John0b4057a2017-11-21 01:46:41 -0500169 @Override
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800170 public synchronized ImmutableMap<DeviceId, List<InterfaceIpAddress>> getGlobalPrefixes() {
Annce John0b4057a2017-11-21 01:46:41 -0500171 return ImmutableMap.copyOf(globalPrefixes);
172 }
173
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800174 @SuppressWarnings("GuardedBy")
175 @GuardedBy(value = "this")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400176 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
177 i -> {
178 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
179 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
180 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
181 globalPrefixes.get(i.connectPoint().deviceId()));
182 } else {
183 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
184 }
185 return prefixEntry;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500186 };
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400187
188 private ScheduledExecutorService executors = null;
189
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400190 private static final String APP_NAME = "org.onosproject.routeradvertisement";
191 private ApplicationId appId;
192
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400193 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
194 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
195 SubjectFactories.DEVICE_SUBJECT_FACTORY,
196 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
197 @Override
198 public RouterAdvertisementDeviceConfig createConfig() {
199
200 return new RouterAdvertisementDeviceConfig();
201 }
202 };
203
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400204 // Listener for handling dynamic interface modifications.
205 private class InternalInterfaceListener implements InterfaceListener {
206 @Override
207 public void event(InterfaceEvent event) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400208 switch (event.type()) {
209 case INTERFACE_ADDED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500210 case INTERFACE_UPDATED:
211 clearTxWorkers();
212 loadGlobalPrefixConfig();
213 setupTxWorkers();
214 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400215 break;
216 case INTERFACE_REMOVED:
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500217 Interface i = event.subject();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400218 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
219 == MastershipRole.MASTER) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500220 deactivateRouterAdvertisement(i.connectPoint());
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400221 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400222 break;
223 default:
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400224 }
225 }
226 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400227
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400228 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
229
230 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400231 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
232 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500233 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay, null, null);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400234 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
235 raThreadDelay, TimeUnit.SECONDS);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500236 transmitters.put(connectPoint, new AbstractMap.SimpleEntry<>(handler, addresses));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400237 }
238
239 // Disables already activated RA threads on 'connectPoint'
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500240 private synchronized List<InterfaceIpAddress> deactivateRouterAdvertisement(ConnectPoint connectPoint) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400241 if (connectPoint != null) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500242 Map.Entry<ScheduledFuture<?>, List<InterfaceIpAddress>> details = transmitters.get(connectPoint);
243 details.getKey().cancel(false);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400244 transmitters.remove(connectPoint);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500245 return details.getValue();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400246 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500247 return null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400248 }
249
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400250 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400251 executors = Executors.newScheduledThreadPool(raPoolSize,
252 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400253 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400254
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400255 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400256 executors.shutdown();
257 }
258
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400259 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400260 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400261 interfaceService.getInterfaces()
262 .stream()
263 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
264 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400265 .map(prefixGenerator::apply)
266 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400267 .stream()
268 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
269 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400270 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400271 );
272 }
273
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400274 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400275 private synchronized void clearTxWorkers() {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500276 transmitters.entrySet().stream().forEach(i -> i.getValue().getKey().cancel(false));
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400277 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400278 }
279
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400280 private synchronized void setupPoolAndTxWorkers() {
281 setupThreadPool();
282 setupTxWorkers();
283 }
284
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400285 private synchronized void clearPoolAndTxWorkers() {
286 clearTxWorkers();
287 clearThreadPool();
288 }
289
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800290 @SuppressWarnings("GuardedBy")
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400291 // Loading global prefixes for devices from network configuration
292 private synchronized void loadGlobalPrefixConfig() {
293 globalPrefixes.clear();
294 Set<DeviceId> deviceSubjects =
295 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
296 deviceSubjects.forEach(subject -> {
297 RouterAdvertisementDeviceConfig config =
298 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
299 if (config != null) {
300 List<InterfaceIpAddress> ips = config.prefixes();
301 globalPrefixes.put(subject, ips);
302 }
303 });
304 }
305
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500306 // Handler for network configuration updates
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400307 private class InternalNetworkConfigListener implements NetworkConfigListener {
308 @Override
309 public void event(NetworkConfigEvent event) {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500310 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)
311 || event.configClass().equals(InterfaceConfig.class)) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400312 switch (event.type()) {
313 case CONFIG_ADDED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400314 case CONFIG_UPDATED:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400315 clearTxWorkers();
316 loadGlobalPrefixConfig();
317 setupTxWorkers();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500318 log.info("Configuration updated for {}", event.subject());
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400319 break;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500320 default:
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400321 }
322 }
323 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400324 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400325
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500326 private final InternalNetworkConfigListener networkConfigListener
327 = new InternalNetworkConfigListener();
328
329 // Handler for device updates
330 private class InternalDeviceListener implements DeviceListener {
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500331 @Override
332 public void event(DeviceEvent event) {
333 switch (event.type()) {
334 case DEVICE_ADDED:
335 case PORT_UPDATED:
336 case PORT_ADDED:
337 case DEVICE_UPDATED:
338 case DEVICE_AVAILABILITY_CHANGED:
339 clearTxWorkers();
340 setupTxWorkers();
341 log.trace("Processed device event {} on {}", event.type(), event.subject());
342 break;
343 default:
344 }
345 }
346 }
347
Charles Chanbac954f92018-04-06 22:29:26 -0700348 private class InternalMastershipListener implements MastershipListener {
349 @Override
350 public void event(MastershipEvent event) {
351 switch (event.type()) {
352 case MASTER_CHANGED:
353 clearTxWorkers();
354 setupTxWorkers();
355 log.trace("Processed mastership event {} on {}", event.type(), event.subject());
356 break;
357 case BACKUPS_CHANGED:
358 case SUSPENDED:
359 default:
360 break;
361 }
362 }
363 }
364
365 private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
366 private final InternalMastershipListener internalMastershipListener = new InternalMastershipListener();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500367
368 // Processor for Solicited RA packets
369 private class InternalPacketProcessor implements PacketProcessor {
370
371 @Override
372 public void process(PacketContext context) {
373 if (context.isHandled()) {
374 return;
375 }
376
377 // Ensure packet is IPv6 Solicited RA
378 InboundPacket pkt = context.inPacket();
379 Ethernet ethernet = pkt.parsed();
380 if ((ethernet == null) || (ethernet.getEtherType() != Ethernet.TYPE_IPV6)) {
381 return;
382 }
383 IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
384 if (ipv6Packet.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
385 return;
386 }
387 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
388 if (icmp6Packet.getIcmpType() != ICMP6.ROUTER_SOLICITATION) {
389 return;
390 }
391
392 // Start solicited-RA handling thread
393 SolicitedRAWorkerThread sraWorkerThread = new SolicitedRAWorkerThread(pkt);
394 executors.schedule(sraWorkerThread, 0, TimeUnit.SECONDS);
395 }
396 }
397
398 InternalPacketProcessor processor = null;
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400399
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400400 @Activate
401 protected void activate(ComponentContext context) {
402 // Basic application registrations.
403 appId = coreService.registerApplication(APP_NAME);
404 componentConfigService.registerProperties(getClass());
405
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500406 // Packet processor for handling Router Solicitations
407 processor = new InternalPacketProcessor();
408 packetService.addProcessor(processor, PacketProcessor.director(3));
409
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400410 // Setup global prefix loading components
411 networkConfigRegistry.addListener(networkConfigListener);
412 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
413 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400414
Charles Chanbac954f92018-04-06 22:29:26 -0700415 // Register device and mastership event listener
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500416 deviceService.addListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700417 mastershipService.addListener(internalMastershipListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500418
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400419 // Setup pool and worker threads for existing interfaces
420 setupPoolAndTxWorkers();
421 }
422
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400423
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400424 @Modified
425 protected void modified(ComponentContext context) {
426 int newRaPoolSize, newRaThreadDelay;
427
428 // Loading configured properties.
429 if (context != null) {
430 Dictionary<?, ?> properties = context.getProperties();
431 try {
432 // Handle change in pool size
433 String s = get(properties, PROP_RA_THREADS_POOL);
434 newRaPoolSize = isNullOrEmpty(s) ?
435 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
436 if (newRaPoolSize != raPoolSize) {
437 raPoolSize = newRaPoolSize;
438 clearPoolAndTxWorkers();
439 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400440 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400441 }
442
443 // Handle change in thread delay
444 s = get(properties, PROP_RA_THREADS_DELAY);
445 newRaThreadDelay = isNullOrEmpty(s) ?
446 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
447 if (newRaThreadDelay != raThreadDelay) {
448 raThreadDelay = newRaThreadDelay;
449 clearTxWorkers();
450 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400451 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400452 }
453
454 // Handle M-flag changes
455 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400456 if (!isNullOrEmpty(s)) {
457 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
458 log.info("RA M-flag set {}", s);
459 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400460
461 // Handle O-flag changes
462 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400463 if (!isNullOrEmpty(s)) {
464 raFlagObitStatus = Boolean.parseBoolean(s.trim());
465 log.info("RA O-flag set {}", s);
466 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400467
468 // Handle prefix option configuration
469 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400470 if (!isNullOrEmpty(s)) {
471 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
472 String status = raOptionPrefixStatus ? "enabled" : "disabled";
473 log.info("RA prefix option {}", status);
474 }
475
476 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
477 if (!isNullOrEmpty(s)) {
478 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
479 clearTxWorkers();
480 setupTxWorkers();
481 String status = raOptionPrefixStatus ? "enabled" : "disabled";
482 log.info("RA global configuration file loading {}", status);
483 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400484
485 } catch (NumberFormatException e) {
486 log.warn("Component configuration had invalid value, aborting changes loading.", e);
487 }
488 }
489 }
490
491 @Deactivate
492 protected void deactivate() {
493 // Unregister resources.
494 componentConfigService.unregisterProperties(getClass(), false);
495 interfaceService.removeListener(interfaceListener);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500496 networkConfigRegistry.removeListener(networkConfigListener);
497 networkConfigRegistry.unregisterConfigFactory(deviceConfigFactory);
498 packetService.removeProcessor(processor);
499 deviceService.removeListener(internalDeviceListener);
Charles Chanbac954f92018-04-06 22:29:26 -0700500 mastershipService.removeListener(internalMastershipListener);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400501
502 // Clear pool & threads
503 clearPoolAndTxWorkers();
504 }
505
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400506 // Worker thread for actually sending ICMPv6 RA packets.
507 private class RAWorkerThread implements Runnable {
508
509 ConnectPoint connectPoint;
510 List<InterfaceIpAddress> ipAddresses;
511 int retransmitPeriod;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500512 MacAddress solicitHostMac;
513 byte[] solicitHostAddress;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400514
515 // Various fixed values in RA packet
516 public static final byte RA_HOP_LIMIT = (byte) 0xff;
517 public static final short RA_ROUTER_LIFETIME = (short) 1800;
518 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
519 public static final int RA_OPTION_MTU_VALUE = 1500;
520 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
521 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
522 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
523
524
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500525 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period,
526 MacAddress macAddress, byte[] ipv6Address) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400527 this.connectPoint = connectPoint;
528 this.ipAddresses = ipAddresses;
529 retransmitPeriod = period;
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500530 solicitHostMac = macAddress;
531 solicitHostAddress = ipv6Address;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400532 }
533
Yuta HIGUCHI488a94c2018-01-26 17:24:09 -0800534 @Override
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400535 public void run() {
536 // Router Advertisement header filling. Please refer RFC-2461.
537 RouterAdvertisement ra = new RouterAdvertisement();
538 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400539 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
540 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400541 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
542 ra.setReachableTime(0);
543 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
544
545 // Option : Source link-layer address.
546 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
547 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
548 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
549 .map(Interface::mac).findFirst();
550 if (!macAddress.isPresent()) {
551 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
552 return;
553 }
554 option.put(macAddress.get().toBytes());
555 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
556 Arrays.copyOfRange(option.array(), 0, option.position()));
557
558 // Option : MTU.
559 option.rewind();
560 option.putShort((short) 0);
561 option.putInt(RA_OPTION_MTU_VALUE);
562 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
563 Arrays.copyOfRange(option.array(), 0, option.position()));
564
565 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400566 if (raOptionPrefixStatus) {
567 ipAddresses.stream()
568 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
569 .forEach(i -> {
570 option.rewind();
571 option.put((byte) i.subnetAddress().prefixLength());
572 // Enable "onlink" option only.
573 option.put((byte) 0x80);
574 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
575 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
576 // Clear reserved fields
577 option.putInt(0x00000000);
578 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
579 i.subnetAddress().prefixLength()).toOctets());
580 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
581 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400582
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400583 });
584 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400585
586 // ICMPv6 header filling.
587 ICMP6 icmpv6 = new ICMP6();
588 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
589 icmpv6.setIcmpCode((byte) 0);
590 icmpv6.setPayload(ra);
591
592 // IPv6 header filling.
593 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
594 IPv6 ipv6 = new IPv6();
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500595 ipv6.setDestinationAddress((solicitHostAddress == null) ? ip6AllNodesAddress : solicitHostAddress);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400596 /* RA packet L2 source address created from port MAC address.
597 * Note : As per RFC-4861 RAs should be sent from link-local address.
598 */
599 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
600 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
601 ipv6.setHopLimit(RA_HOP_LIMIT);
602 ipv6.setTrafficClass((byte) 0xe0);
603 ipv6.setPayload(icmpv6);
604
605 // Ethernet header filling.
606 Ethernet ethernet = new Ethernet();
607
608 /* Ethernet IPv6 multicast address creation.
609 * Refer : RFC 2624 section 7.
610 */
611 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
612 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
613 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500614 // Provide unicast address for Solicit RA replays
615 ethernet.setDestinationMACAddress((solicitHostMac == null) ?
616 MacAddress.valueOf(l2Ipv6MulticastAddress) : solicitHostMac);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400617 ethernet.setSourceMACAddress(macAddress.get().toBytes());
618 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
619 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
620 ethernet.setPayload(ipv6);
621 ethernet.setPad(false);
622
623 // Flush out PACKET_OUT.
624 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
625 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
626 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
627 treatment, stream);
628 packetService.emit(packet);
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500629 log.trace("Transmitted Unsolicited Router Advertisement on {}", connectPoint);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400630 }
631 }
Jayakumar Thazhathe166a4e2017-11-15 02:17:04 -0500632
633 // Worker thread for processing IPv6 Solicited RA packets.
634 public class SolicitedRAWorkerThread implements Runnable {
635
636 private InboundPacket packet;
637
638 SolicitedRAWorkerThread(InboundPacket packet) {
639 this.packet = packet;
640 }
641
642 @Override
643 public void run() {
644
645 // TODO : Validate Router Solicitation
646
647 // Pause already running unsolicited RA threads in received connect point
648 ConnectPoint connectPoint = packet.receivedFrom();
649 List<InterfaceIpAddress> addresses = deactivateRouterAdvertisement(connectPoint);
650
651 /* Multicast RA(ie. Unsolicited RA) TX time is not preciously tracked so to make sure that
652 * Unicast RA(ie. Router Solicitation Response) is TXed before Mulicast RA
653 * logic adapted here is disable Mulicast RA, TX Unicast RA and then restore Multicast RA.
654 */
655 log.trace("Processing Router Solicitations from {}", connectPoint);
656 try {
657 Ethernet ethernet = packet.parsed();
658 IPv6 ipv6 = (IPv6) ethernet.getPayload();
659 RAWorkerThread worker = new RAWorkerThread(connectPoint,
660 addresses, raThreadDelay, ethernet.getSourceMAC(), ipv6.getSourceAddress());
661 // TODO : Estimate TX time as in RFC 4861, Section 6.2.6 and schedule TX based on it
662 CompletableFuture<Void> sraHandlerFuture = CompletableFuture.runAsync(worker, executors);
663 sraHandlerFuture.get();
664 } catch (Exception e) {
665 log.error("Failed to respond to router solicitation. {}", e);
666 } finally {
667 activateRouterAdvertisement(connectPoint, addresses);
668 log.trace("Restored Unsolicited Router Advertisements on {}", connectPoint);
669 }
670 }
671 }
672
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400673}