blob: 91aea804b4ef52067572960ad2fa40651df73c0b [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;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Property;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
jayakumarthazhath1cbde732017-10-25 10:01:00 -040025import org.apache.felix.scr.annotations.Modified;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040026import org.onlab.packet.EthType;
27import org.onlab.packet.Ethernet;
28import org.onlab.packet.ICMP6;
29import org.onlab.packet.IPv6;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.Ip6Address;
32import org.onlab.packet.MacAddress;
33import org.onlab.packet.ndp.RouterAdvertisement;
34import org.onlab.packet.ndp.NeighborDiscoveryOptions;
35import org.onosproject.cfg.ComponentConfigService;
36import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.mastership.MastershipService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040039import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040040import org.onosproject.net.MastershipRole;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040041import org.onosproject.net.config.ConfigFactory;
42import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
44import org.onosproject.net.config.NetworkConfigRegistry;
45import org.onosproject.net.config.basics.SubjectFactories;
46import org.onosproject.net.device.DeviceService;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040047import org.onosproject.net.intf.Interface;
48import org.onosproject.net.intf.InterfaceEvent;
49import org.onosproject.net.intf.InterfaceListener;
50import org.onosproject.net.intf.InterfaceService;
51import org.onosproject.net.ConnectPoint;
52import org.onosproject.net.flow.DefaultTrafficTreatment;
53import org.onosproject.net.flow.TrafficTreatment;
54import org.onosproject.net.host.InterfaceIpAddress;
55import org.onosproject.net.packet.DefaultOutboundPacket;
56import org.onosproject.net.packet.OutboundPacket;
57import org.onosproject.net.packet.PacketService;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040058import org.onosproject.ra.config.RouterAdvertisementDeviceConfig;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040059import org.slf4j.Logger;
60import org.slf4j.LoggerFactory;
61import org.osgi.service.component.ComponentContext;
62
63import javax.annotation.concurrent.GuardedBy;
64import java.nio.ByteBuffer;
65import java.util.Dictionary;
66import java.util.LinkedHashMap;
67import java.util.List;
68import java.util.Map;
69import java.util.Arrays;
70import java.util.Optional;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040071import java.util.Set;
72import java.util.AbstractMap;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040073
74import java.util.concurrent.ScheduledExecutorService;
75import java.util.concurrent.ScheduledFuture;
76import java.util.concurrent.Executors;
77import java.util.concurrent.TimeUnit;
Jayakumar Thazhath28484572017-11-02 00:39:09 -040078import java.util.function.Function;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040079import java.util.stream.IntStream;
80
81import static com.google.common.base.Strings.isNullOrEmpty;
82import static org.onlab.util.Tools.get;
83import static org.onlab.util.Tools.groupedThreads;
84
85/**
86 * Manages IPv6 Router Advertisements.
87 */
88@Component(immediate = true)
89public class RouterAdvertisementManager {
90
91 private final Logger log = LoggerFactory.getLogger(getClass());
92 private static final String PROP_RA_THREADS_POOL = "raPoolSize";
93 private static final int DEFAULT_RA_THREADS_POOL_SIZE = 10;
94 private static final String PROP_RA_THREADS_DELAY = "raThreadDelay";
95 private static final int DEFAULT_RA_THREADS_DELAY = 5;
jayakumarthazhath1cbde732017-10-25 10:01:00 -040096 private static final String PROP_RA_FLAG_MBIT_STATUS = "raFlagMbitStatus";
97 private static final boolean DEFAULT_RA_FLAG_MBIT_STATUS = false;
98 private static final String PROP_RA_FLAG_OBIT_STATUS = "raFlagObitStatus";
99 private static final boolean DEFAULT_RA_FLAG_OBIT_STATUS = false;
100 private static final String PROP_RA_OPTION_PREFIX_STATUS = "raOptionPrefixStatus";
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400101 private static final boolean DEFAULT_RA_OPTION_PREFIX_STATUS = false;
102 private static final String PROP_RA_GLOBAL_PREFIX_CONF_STATUS = "raGlobalPrefixConfStatus";
103 private static final boolean DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS = true;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected CoreService coreService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 PacketService packetService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected ComponentConfigService componentConfigService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 public InterfaceService interfaceService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 public MastershipService mastershipService;
119
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 public DeviceService deviceService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected NetworkConfigRegistry networkConfigRegistry;
125
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400126 @Property(name = PROP_RA_THREADS_POOL, intValue = DEFAULT_RA_THREADS_POOL_SIZE,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400127 label = "Thread pool capacity")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400128 protected int raPoolSize = DEFAULT_RA_THREADS_POOL_SIZE;
129
130 @Property(name = PROP_RA_THREADS_DELAY, intValue = DEFAULT_RA_THREADS_DELAY,
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400131 label = "Thread delay in seconds")
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400132 protected int raThreadDelay = DEFAULT_RA_THREADS_DELAY;
133
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400134 @Property(name = PROP_RA_FLAG_MBIT_STATUS, boolValue = DEFAULT_RA_FLAG_MBIT_STATUS,
135 label = "Turn M-bit flag on/off")
136 protected boolean raFlagMbitStatus = DEFAULT_RA_FLAG_MBIT_STATUS;
137
138 @Property(name = PROP_RA_FLAG_OBIT_STATUS, boolValue = DEFAULT_RA_FLAG_OBIT_STATUS,
139 label = "Turn O-bit flag on/off")
140 protected boolean raFlagObitStatus = DEFAULT_RA_FLAG_OBIT_STATUS;
141
142 @Property(name = PROP_RA_OPTION_PREFIX_STATUS, boolValue = DEFAULT_RA_OPTION_PREFIX_STATUS,
143 label = "Prefix option support needed or not")
144 protected boolean raOptionPrefixStatus = DEFAULT_RA_OPTION_PREFIX_STATUS;
145
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400146 @Property(name = PROP_RA_GLOBAL_PREFIX_CONF_STATUS, boolValue = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS,
147 label = "Global prefix configuration support on/off")
148 protected boolean raGlobalConfigStatus = DEFAULT_RA_GLOBAL_PREFIX_CONF_STATUS;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400149
150 @GuardedBy(value = "this")
151 private final Map<ConnectPoint, ScheduledFuture<?>> transmitters = new LinkedHashMap<>();
152
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400153 @GuardedBy(value = "this")
154 private final Map<DeviceId, List<InterfaceIpAddress>> globalPrefixes = new LinkedHashMap<>();
155
156 private Function<Interface, Map.Entry<ConnectPoint, List<InterfaceIpAddress>>> prefixGenerator =
157 i -> {
158 Map.Entry<ConnectPoint, List<InterfaceIpAddress>> prefixEntry;
159 if (raGlobalConfigStatus && globalPrefixes.containsKey(i.connectPoint().deviceId())) {
160 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(),
161 globalPrefixes.get(i.connectPoint().deviceId()));
162 } else {
163 prefixEntry = new AbstractMap.SimpleEntry<>(i.connectPoint(), i.ipAddressesList());
164 }
165 return prefixEntry;
166 };
167
168 private ScheduledExecutorService executors = null;
169
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400170 private static final String APP_NAME = "org.onosproject.routeradvertisement";
171 private ApplicationId appId;
172
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400173 private final ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig> deviceConfigFactory =
174 new ConfigFactory<DeviceId, RouterAdvertisementDeviceConfig>(
175 SubjectFactories.DEVICE_SUBJECT_FACTORY,
176 RouterAdvertisementDeviceConfig.class, "routeradvertisement") {
177 @Override
178 public RouterAdvertisementDeviceConfig createConfig() {
179
180 return new RouterAdvertisementDeviceConfig();
181 }
182 };
183
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400184 // Listener for handling dynamic interface modifications.
185 private class InternalInterfaceListener implements InterfaceListener {
186 @Override
187 public void event(InterfaceEvent event) {
188 Interface i = event.subject();
189 switch (event.type()) {
190 case INTERFACE_ADDED:
191 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
192 == MastershipRole.MASTER) {
193 activateRouterAdvertisement(i.connectPoint(),
194 i.ipAddressesList());
195 }
196 break;
197 case INTERFACE_REMOVED:
198 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
199 == MastershipRole.MASTER) {
200 deactivateRouterAdvertisement(i.connectPoint(),
201 i.ipAddressesList());
202 }
203 break;
204 case INTERFACE_UPDATED:
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400205 if (mastershipService.getLocalRole(i.connectPoint().deviceId())
206 == MastershipRole.MASTER) {
207 deactivateRouterAdvertisement(i.connectPoint(),
208 i.ipAddressesList());
209 activateRouterAdvertisement(i.connectPoint(),
210 i.ipAddressesList());
211 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400212 break;
213 default:
214 break;
215 }
216 }
217 }
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400218
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400219 private final InterfaceListener interfaceListener = new InternalInterfaceListener();
220
221 // Enables RA threads on 'connectPoint' with configured IPv6s
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400222 private synchronized void activateRouterAdvertisement(ConnectPoint connectPoint,
223 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400224 RAWorkerThread worker = new RAWorkerThread(connectPoint, addresses, raThreadDelay);
225 ScheduledFuture<?> handler = executors.scheduleAtFixedRate(worker, raThreadDelay,
226 raThreadDelay, TimeUnit.SECONDS);
227 transmitters.put(connectPoint, handler);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400228 }
229
230 // Disables already activated RA threads on 'connectPoint'
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400231 private synchronized void deactivateRouterAdvertisement(ConnectPoint connectPoint,
232 List<InterfaceIpAddress> addresses) {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400233 // Note : Parameter addresses not used now, kept for future.
234 if (connectPoint != null) {
235 ScheduledFuture<?> handler = transmitters.get(connectPoint);
236 handler.cancel(false);
237 transmitters.remove(connectPoint);
238 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400239 }
240
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400241 private synchronized void setupThreadPool() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400242 executors = Executors.newScheduledThreadPool(raPoolSize,
243 groupedThreads("RouterAdvertisement", "event-%d", log));
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400244 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400245
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400246 private synchronized void clearThreadPool() {
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400247 executors.shutdown();
248 }
249
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400250 // Start Tx threads for all configured interfaces.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400251 private synchronized void setupTxWorkers() {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400252 interfaceService.getInterfaces()
253 .stream()
254 .filter(i -> mastershipService.getLocalRole(i.connectPoint().deviceId())
255 == MastershipRole.MASTER)
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400256 .map(prefixGenerator::apply)
257 .filter(i -> i.getValue()
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400258 .stream()
259 .anyMatch(ia -> ia.ipAddress().version().equals(IpAddress.Version.INET6)))
260 .forEach(j ->
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400261 activateRouterAdvertisement(j.getKey(), j.getValue())
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400262 );
263 }
264
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400265 // Clear out Tx threads.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400266 private synchronized void clearTxWorkers() {
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400267 transmitters.entrySet().stream().forEach(i -> i.getValue().cancel(false));
268 transmitters.clear();
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400269 }
270
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400271 private synchronized void setupPoolAndTxWorkers() {
272 setupThreadPool();
273 setupTxWorkers();
274 }
275
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400276 private synchronized void clearPoolAndTxWorkers() {
277 clearTxWorkers();
278 clearThreadPool();
279 }
280
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400281 // Loading global prefixes for devices from network configuration
282 private synchronized void loadGlobalPrefixConfig() {
283 globalPrefixes.clear();
284 Set<DeviceId> deviceSubjects =
285 networkConfigRegistry.getSubjects(DeviceId.class, RouterAdvertisementDeviceConfig.class);
286 deviceSubjects.forEach(subject -> {
287 RouterAdvertisementDeviceConfig config =
288 networkConfigRegistry.getConfig(subject, RouterAdvertisementDeviceConfig.class);
289 if (config != null) {
290 List<InterfaceIpAddress> ips = config.prefixes();
291 globalPrefixes.put(subject, ips);
292 }
293 });
294 }
295
296 private class InternalNetworkConfigListener implements NetworkConfigListener {
297 @Override
298 public void event(NetworkConfigEvent event) {
299 if (event.configClass().equals(RouterAdvertisementDeviceConfig.class)) {
300 switch (event.type()) {
301 case CONFIG_ADDED:
302 log.info("Router Advertisement device Config added for {}", event.subject());
303 clearTxWorkers();
304 loadGlobalPrefixConfig();
305 setupTxWorkers();
306 break;
307 case CONFIG_UPDATED:
308 log.info("Router Advertisement device updated for {}", event.subject());
309 clearTxWorkers();
310 loadGlobalPrefixConfig();
311 setupTxWorkers();
312 break;
313 default :
314 break;
315 }
316 }
317 }
318
319 }
320 private final InternalNetworkConfigListener networkConfigListener
321 = new InternalNetworkConfigListener();
322
323
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400324 @Activate
325 protected void activate(ComponentContext context) {
326 // Basic application registrations.
327 appId = coreService.registerApplication(APP_NAME);
328 componentConfigService.registerProperties(getClass());
329
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400330 // Setup global prefix loading components
331 networkConfigRegistry.addListener(networkConfigListener);
332 networkConfigRegistry.registerConfigFactory(deviceConfigFactory);
333 loadGlobalPrefixConfig();
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400334
335 // Setup pool and worker threads for existing interfaces
336 setupPoolAndTxWorkers();
337 }
338
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400339
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400340 @Modified
341 protected void modified(ComponentContext context) {
342 int newRaPoolSize, newRaThreadDelay;
343
344 // Loading configured properties.
345 if (context != null) {
346 Dictionary<?, ?> properties = context.getProperties();
347 try {
348 // Handle change in pool size
349 String s = get(properties, PROP_RA_THREADS_POOL);
350 newRaPoolSize = isNullOrEmpty(s) ?
351 DEFAULT_RA_THREADS_POOL_SIZE : Integer.parseInt(s.trim());
352 if (newRaPoolSize != raPoolSize) {
353 raPoolSize = newRaPoolSize;
354 clearPoolAndTxWorkers();
355 setupPoolAndTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400356 log.info("Thread pool size updated to {}", raPoolSize);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400357 }
358
359 // Handle change in thread delay
360 s = get(properties, PROP_RA_THREADS_DELAY);
361 newRaThreadDelay = isNullOrEmpty(s) ?
362 DEFAULT_RA_THREADS_DELAY : Integer.parseInt(s.trim());
363 if (newRaThreadDelay != raThreadDelay) {
364 raThreadDelay = newRaThreadDelay;
365 clearTxWorkers();
366 setupTxWorkers();
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400367 log.info("Thread delay updated to {}", raThreadDelay);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400368 }
369
370 // Handle M-flag changes
371 s = get(properties, PROP_RA_FLAG_MBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400372 if (!isNullOrEmpty(s)) {
373 raFlagMbitStatus = Boolean.parseBoolean(s.trim());
374 log.info("RA M-flag set {}", s);
375 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400376
377 // Handle O-flag changes
378 s = get(properties, PROP_RA_FLAG_OBIT_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400379 if (!isNullOrEmpty(s)) {
380 raFlagObitStatus = Boolean.parseBoolean(s.trim());
381 log.info("RA O-flag set {}", s);
382 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400383
384 // Handle prefix option configuration
385 s = get(properties, PROP_RA_OPTION_PREFIX_STATUS);
Jayakumar Thazhath28484572017-11-02 00:39:09 -0400386 if (!isNullOrEmpty(s)) {
387 raOptionPrefixStatus = Boolean.parseBoolean(s.trim());
388 String status = raOptionPrefixStatus ? "enabled" : "disabled";
389 log.info("RA prefix option {}", status);
390 }
391
392 s = get(properties, PROP_RA_GLOBAL_PREFIX_CONF_STATUS);
393 if (!isNullOrEmpty(s)) {
394 raGlobalConfigStatus = Boolean.parseBoolean(s.trim());
395 clearTxWorkers();
396 setupTxWorkers();
397 String status = raOptionPrefixStatus ? "enabled" : "disabled";
398 log.info("RA global configuration file loading {}", status);
399 }
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400400
401 } catch (NumberFormatException e) {
402 log.warn("Component configuration had invalid value, aborting changes loading.", e);
403 }
404 }
405 }
406
407 @Deactivate
408 protected void deactivate() {
409 // Unregister resources.
410 componentConfigService.unregisterProperties(getClass(), false);
411 interfaceService.removeListener(interfaceListener);
412
413 // Clear pool & threads
414 clearPoolAndTxWorkers();
415 }
416
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400417 // Worker thread for actually sending ICMPv6 RA packets.
418 private class RAWorkerThread implements Runnable {
419
420 ConnectPoint connectPoint;
421 List<InterfaceIpAddress> ipAddresses;
422 int retransmitPeriod;
423
424 // Various fixed values in RA packet
425 public static final byte RA_HOP_LIMIT = (byte) 0xff;
426 public static final short RA_ROUTER_LIFETIME = (short) 1800;
427 public static final int RA_OPTIONS_BUFFER_SIZE = 500;
428 public static final int RA_OPTION_MTU_VALUE = 1500;
429 public static final int RA_OPTION_PREFIX_VALID_LIFETIME = 600;
430 public static final int RA_OPTION_PREFIX_PREFERRED_LIFETIME = 600;
431 public static final int RA_RETRANSMIT_CALIBRATION_PERIOD = 1;
432
433
434 RAWorkerThread(ConnectPoint connectPoint, List<InterfaceIpAddress> ipAddresses, int period) {
435 this.connectPoint = connectPoint;
436 this.ipAddresses = ipAddresses;
437 retransmitPeriod = period;
438 }
439
440 public void run() {
441 // Router Advertisement header filling. Please refer RFC-2461.
442 RouterAdvertisement ra = new RouterAdvertisement();
443 ra.setCurrentHopLimit(RA_HOP_LIMIT);
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400444 ra.setMFlag((byte) (raFlagMbitStatus ? 0x01 : 0x00));
445 ra.setOFlag((byte) (raFlagObitStatus ? 0x01 : 0x00));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400446 ra.setRouterLifetime(RA_ROUTER_LIFETIME);
447 ra.setReachableTime(0);
448 ra.setRetransmitTimer(retransmitPeriod + RA_RETRANSMIT_CALIBRATION_PERIOD);
449
450 // Option : Source link-layer address.
451 byte[] optionBuffer = new byte[RA_OPTIONS_BUFFER_SIZE];
452 ByteBuffer option = ByteBuffer.wrap(optionBuffer);
453 Optional<MacAddress> macAddress = interfaceService.getInterfacesByPort(connectPoint).stream()
454 .map(Interface::mac).findFirst();
455 if (!macAddress.isPresent()) {
456 log.warn("Unable to retrieve interface {} MAC address. Terminating RA transmission.", connectPoint);
457 return;
458 }
459 option.put(macAddress.get().toBytes());
460 ra.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
461 Arrays.copyOfRange(option.array(), 0, option.position()));
462
463 // Option : MTU.
464 option.rewind();
465 option.putShort((short) 0);
466 option.putInt(RA_OPTION_MTU_VALUE);
467 ra.addOption(NeighborDiscoveryOptions.TYPE_MTU,
468 Arrays.copyOfRange(option.array(), 0, option.position()));
469
470 // Option : Prefix information.
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400471 if (raOptionPrefixStatus) {
472 ipAddresses.stream()
473 .filter(i -> i.ipAddress().version().equals(IpAddress.Version.INET6))
474 .forEach(i -> {
475 option.rewind();
476 option.put((byte) i.subnetAddress().prefixLength());
477 // Enable "onlink" option only.
478 option.put((byte) 0x80);
479 option.putInt(RA_OPTION_PREFIX_VALID_LIFETIME);
480 option.putInt(RA_OPTION_PREFIX_PREFERRED_LIFETIME);
481 // Clear reserved fields
482 option.putInt(0x00000000);
483 option.put(IpAddress.makeMaskedAddress(i.ipAddress(),
484 i.subnetAddress().prefixLength()).toOctets());
485 ra.addOption(NeighborDiscoveryOptions.TYPE_PREFIX_INFORMATION,
486 Arrays.copyOfRange(option.array(), 0, option.position()));
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400487
jayakumarthazhath1cbde732017-10-25 10:01:00 -0400488 });
489 }
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400490
491 // ICMPv6 header filling.
492 ICMP6 icmpv6 = new ICMP6();
493 icmpv6.setIcmpType(ICMP6.ROUTER_ADVERTISEMENT);
494 icmpv6.setIcmpCode((byte) 0);
495 icmpv6.setPayload(ra);
496
497 // IPv6 header filling.
498 byte[] ip6AllNodesAddress = Ip6Address.valueOf("ff02::1").toOctets();
499 IPv6 ipv6 = new IPv6();
500 ipv6.setDestinationAddress(ip6AllNodesAddress);
501 /* RA packet L2 source address created from port MAC address.
502 * Note : As per RFC-4861 RAs should be sent from link-local address.
503 */
504 ipv6.setSourceAddress(IPv6.getLinkLocalAddress(macAddress.get().toBytes()));
505 ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6);
506 ipv6.setHopLimit(RA_HOP_LIMIT);
507 ipv6.setTrafficClass((byte) 0xe0);
508 ipv6.setPayload(icmpv6);
509
510 // Ethernet header filling.
511 Ethernet ethernet = new Ethernet();
512
513 /* Ethernet IPv6 multicast address creation.
514 * Refer : RFC 2624 section 7.
515 */
516 byte[] l2Ipv6MulticastAddress = MacAddress.IPV6_MULTICAST.toBytes();
517 IntStream.range(1, 4).forEach(i -> l2Ipv6MulticastAddress[l2Ipv6MulticastAddress.length - i] =
518 ip6AllNodesAddress[ip6AllNodesAddress.length - i]);
519
520 ethernet.setDestinationMACAddress(MacAddress.valueOf(l2Ipv6MulticastAddress));
521 ethernet.setSourceMACAddress(macAddress.get().toBytes());
522 ethernet.setEtherType(EthType.EtherType.IPV6.ethType().toShort());
523 ethernet.setVlanID(Ethernet.VLAN_UNTAGGED);
524 ethernet.setPayload(ipv6);
525 ethernet.setPad(false);
526
527 // Flush out PACKET_OUT.
528 ByteBuffer stream = ByteBuffer.wrap(ethernet.serialize());
529 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
530 OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
531 treatment, stream);
532 packetService.emit(packet);
533 }
534 }
535}