From e6d01e16f4b791e585112a8edd0724dd10a7eddf Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 4 Oct 2019 11:54:56 -0700 Subject: [PATCH 1/3] ICMP6 Router Advertisement feature This commit adds "Router Advertisement" feature which emits periodic Neighbor Discovery ICMPv6 Router Advertisement (RA) messages announcing routes on other network interfaces related to Thread network. The routes included in RA message mirror all the routes added on the host primary interface (from `IPv6:Routes`) corresponding to off-mesh routes and/or on-mesh prefixes within the Thread network. This feature can be enabled through property `RouterAdvert:Enable` (by default it is disabled) and its behavior can modified through a group of wpan properties (all start with prefix `RouterAdver`). A readme/guide is also added under doc folder which describes the wpan properties and shows example use/behavior of this feature. --- Android.mk | 1 + doc/router-advert-feature-guide.md | 170 +++++++++++++ .../SpinelNCPInstance-Protothreads.cpp | 6 + src/util/netif-mgmt.c | 21 ++ src/util/netif-mgmt.h | 2 + src/wpantund/ICMP6RouterAdvertiser.cpp | 230 ++++++++++++++++++ src/wpantund/ICMP6RouterAdvertiser.h | 86 +++++++ src/wpantund/Makefile.am | 2 + src/wpantund/NCPInstanceBase-Addresses.cpp | 3 + src/wpantund/NCPInstanceBase-AsyncIO.cpp | 18 +- src/wpantund/NCPInstanceBase.cpp | 137 ++++++++++- src/wpantund/NCPInstanceBase.h | 17 ++ src/wpantund/wpan-properties.h | 6 + 13 files changed, 697 insertions(+), 2 deletions(-) create mode 100644 doc/router-advert-feature-guide.md create mode 100644 src/wpantund/ICMP6RouterAdvertiser.cpp create mode 100644 src/wpantund/ICMP6RouterAdvertiser.h diff --git a/Android.mk b/Android.mk index d5237050..3d3229b2 100644 --- a/Android.mk +++ b/Android.mk @@ -89,6 +89,7 @@ LOCAL_SRC_FILES := \ src/wpantund/NCPInstance.cpp \ src/wpantund/NCPInstanceBase.cpp \ src/wpantund/FirmwareUpgrade.cpp \ + src/wpantund/ICMP6RouterAdvertiser.cpp \ src/wpantund/StatCollector.cpp \ src/wpantund/RunawayResetBackoffManager.cpp \ src/wpantund/NCPInstanceBase-NetInterface.cpp \ diff --git a/doc/router-advert-feature-guide.md b/doc/router-advert-feature-guide.md new file mode 100644 index 00000000..c48d099d --- /dev/null +++ b/doc/router-advert-feature-guide.md @@ -0,0 +1,170 @@ +# `wpantund` ICMPv6 Router Advertisement Feature # + +The `"Router Advertisement"` feature enables `wpantund` to emit periodic Neighbor Discovery ICMPv6 Router Advertisement (RA) messages announcing routes on other network interfaces related to Thread network routes. + +The routes included in RA message mirror all the routes added on the host primary interface corresponding to the Thread network: +- Host routes associated with off-mesh routes within the Thread network (when `Daemon:OffMeshRoute:AutoAddOnInterface` feature is enabled). +- Host routes associated with on-mesh prefixes within the Thread network (when `Daemon:OnMeshPrefix:AutoAddAsInterfaceRoute` feature is enabled). +- The list of interface routes is available form wpan property `IPv6:Routes`. + +The wpantund RA feature can be enabled through property `RouterAdvert:Enable` (by default it is disabled) and its behavior can modified through a group of wpan properties (all starting with `RouterAdvert:` prefix). + +- `RouterAdvert:Enable` can be used to enable or disable the whole feature (default is false, i.e., disabled) +- `RouterAdvert:Netifs` defines the list of netif names to send RA messages on. This is a list-based property (we can set the entire list using space separated interface names, or use `add` or `remove` commands to update the list item by item). On start the list is empty. +- `RouterAdvert:TxPeriod` the tx period of RA messages in units of seconds. Minimum period is 4 seconds, max period is 1800 seconds. The period is set to min or max if the value being set is out of the supported range. On start it is set to 10 seconds. +- `RouterAdvert:DefaultRoute:Lifetime` specifies the lifetime value in RA header (non-zero indicates that we are a default route). By default it is set to zero (i.e. not a default route). +- `RouterAdvert:DefaultRoute:Preference` specifies the default route preference. Positive value indicates high, zero indicates medium, and negative indicates low preference. Default value is zero (medium). + +# Example of behavior + +We enable the feature: + + wpanctl:wpan1> set RouterAdvert:Enable true + +Add the netif names we want ICMPv6 RA messages to be sent on (this is a list): + + wpanctl:wpan1> add RouterAdvert:Netifs wpan1 + wpanctl:wpan1> get RouterAdvert:Netifs + RouterAdvert:Netifs = [ + "wpan1" + ] + +When a route is added on host, the same route is announced through the RA message: + + wpanctl:wpan1> add-prefix fd00:1234:: -l 64 -o -c + Successfully added prefix "fd00:1234::" len:64 stable:0 [on-mesh:1 def-route:0 config:1 dhcp:0 slaac:0 pref:0 prio:med] + wpanctl:wpan1> + wpanctl:wpan1> + + wpanctl:wpan1> get IPv6:Routes + IPv6:Routes = [ + "fd00:1234::/64 metric:256 " + ] + +Output of `tcpdump` on the interface shows the RA message: + + sudo tcpdump -n -i wpan1 icmp6 -vv -X + + 11:01:58.991522 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 40) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + route info option (24), length 16 (2): fd00:1234::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 1234 0000 0000 + 0x0000: 600b a663 0028 3a01 fe80 0000 0000 0000 `..c.(:......... + 0x0010: 4801 7e22 7895 a656 ff02 0000 0000 0000 H.~"x..V........ + 0x0020: 0000 0000 0000 0001 8600 13af ff00 0000 ................ + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 1802 4000 0000 0e10 fd00 1234 0000 0000 ..@........4.... + +We can add or remove interface names to `RouterAdvert:Netifs` property. The RA messages are sent over all given interfaces: + + wpanctl:wpan1> add RouterAdvert:Netifs eno1 + + wpanctl:wpan1> + wpanctl:wpan1> get RouterAdvert:Netifs + RouterAdvert:Netifs = [ + "eno1" + "wpan1" + ] + + sudo tcpdump -n -i eno1 icmp6 -vv -X + + 11:22:23.785865 IP6 (flowlabel 0xbcd66, hlim 1, next-header ICMPv6 (58) payload length: 40) fe80::6d87:d70b:c949:762a > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): ec:b1:d7:2f:7c:b9 + 0x0000: ecb1 d72f 7cb9 + route info option (24), length 16 (2): fd00:1234::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 1234 0000 0000 + 0x0000: 600b cd66 0028 3a01 fe80 0000 0000 0000 `..f.(:......... + 0x0010: 6d87 d70b c949 762a ff02 0000 0000 0000 m....Iv*........ + 0x0020: 0000 0000 0000 0001 8600 341c ff00 0000 ..........4..... + 0x0030: 0000 0e10 0000 0000 0101 ecb1 d72f 7cb9 ............./|. + 0x0040: 1802 4000 0000 0e10 fd00 1234 0000 0000 ..@........4.... + +The `wpantund` logs indicate when a RA message is sent over an interface: + + wpantund[69029]: Sent ICMP6 RouterAdvert on "eno1" (1 route info options) + wpantund[69029]: Sent ICMP6 RouterAdvert on "wpan1" (1 route info options) + +The entire list of interfaces can be set in one command (via space separated list of names). + + wpanctl:wpan1> set RouterAdvert:Netifs "wpan1 eno1" + + wpanctl:wpan1> get RouterAdvert:Netifs + RouterAdvert:Netifs = [ + "eno1" + "wpan1" + ] + +RA messages are sent every `RouterAdvert:TxPeriod` seconds or when a route is added/removed (some state changes) + + wpanctl:wpan1> set RouterAdvert:TxPeriod 4 + +When a route is added/removed (state changes), a new RA is sent immediately. + + wpanctl:wpan1> add-prefix fd00:baba:cafe:: -l 48 -o -c + Successfully added prefix "fd00:baba:cafe::" len:48 stable:0 [on-mesh:1 def-route:0 config:1 dhcp:0 slaac:0 pref:0 prio:med] + + wpanctl:wpan1> get IPv6:Routes + IPv6:Routes = [ + "fd00:baba:cafe::/48 metric:256 " + "fd00:1234::/64 metric:256 " + ] + +`tcpdump` output now contains two route info options and is sent every 4 seconds: + + 11:07:04.441659 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + route info option (24), length 16 (2): fd00:baba:cafe::/48, pref=medium, lifetime=3600s + 0x0000: 3000 0000 0e10 fd00 baba cafe 0000 + route info option (24), length 16 (2): fd00:1234::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 1234 0000 0000 + 0x0000: 600b a663 0038 3a01 fe80 0000 0000 0000 `..c.8:......... + 0x0010: 4801 7e22 7895 a656 ff02 0000 0000 0000 H.~"x..V........ + 0x0020: 0000 0000 0000 0001 8600 3ad2 ff00 0000 ..........:..... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 1802 3000 0000 0e10 fd00 baba cafe 0000 ..0............. + 0x0050: 1802 4000 0000 0e10 fd00 1234 0000 0000 ..@........4.... + + 11:07:08.445622 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + route info option (24), length 16 (2): fd00:baba:cafe::/48, pref=medium, lifetime=3600s + 0x0000: 3000 0000 0e10 fd00 baba cafe 0000 + route info option (24), length 16 (2): fd00:1234::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 1234 0000 0000 + 0x0000: 600b a663 0038 3a01 fe80 0000 0000 0000 `..c.8:......... + 0x0010: 4801 7e22 7895 a656 ff02 0000 0000 0000 H.~"x..V........ + 0x0020: 0000 0000 0000 0001 8600 3ad2 ff00 0000 ..........:..... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 1802 3000 0000 0e10 fd00 baba cafe 0000 ..0............. + 0x0050: 1802 4000 0000 0e10 fd00 1234 0000 0000 ..@........4.... + + +The property `RouterAdvert:DefaultRoute:Lifetime` specifies the lifetime value in RA header (non-zero value indicates that this router is a default route): + + wpanctl:wpan1> set RouterAdvert:DefaultRoute:Lifetime 1000 + + 11:10:05.929627 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 1000s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + +The property `RouterAdvert:DefaultRoute:Preference` determines default route preference. Positive value indicates high, zero indicates medium, and negative indicates low preference. + + wpanctl:wpan1> set RouterAdvert:DefaultRoute:Preference 1 + + 11:12:19.989599 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref high, router lifetime 1000s, reachable time 3600s, retrans time 0s + + wpanctl:wpan1> set RouterAdvert:DefaultRoute:Preference -v -1 + + 11:13:08.078489 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref low, router lifetime 1000s, reachable time 3600s, retrans time 0s + +Limitations: +- The current implementation does not support replying to Router Solicitation messages. diff --git a/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp b/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp index 978a3e1e..16cf7ff8 100644 --- a/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp +++ b/src/ncp-spinel/SpinelNCPInstance-Protothreads.cpp @@ -560,12 +560,18 @@ SpinelNCPInstance::vprocess_init(int event, va_list args) int SpinelNCPInstance::vprocess_event(int event, va_list args) { + va_list args_tmp; + if (get_ncp_state() == FAULT) { // We perform no processing in the fault state. PT_INIT(&mControlPT); return 0; } + va_copy(args_tmp, args); + NCPInstanceBase::vprocess_event(event, args_tmp); + va_end(args_tmp); + while (!mTaskQueue.empty()) { va_list tmp; boost::shared_ptr current_task(mTaskQueue.front()); diff --git a/src/util/netif-mgmt.c b/src/util/netif-mgmt.c index cb5baeba..2ec9fdf5 100644 --- a/src/util/netif-mgmt.c +++ b/src/util/netif-mgmt.c @@ -532,3 +532,24 @@ netif_mgmt_leave_ipv6_multicast_address(int reqfd, const char* if_name, const ui bail: return ret; } + +int +netif_mgmt_get_hw_address(int fd, const char* if_name, uint8_t addr[6]) +{ + int ret = -1; + +#ifdef SIOCGIFHWADDR + struct ifreq ifr; + + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); + + ret = ioctl(fd, SIOCGIFHWADDR, &ifr); + + if (ret >= 0) { + memcpy(addr, ifr.ifr_hwaddr.sa_data, 6); + } +#endif + + return ret; +} diff --git a/src/util/netif-mgmt.h b/src/util/netif-mgmt.h index 710f87f9..0a6b2763 100644 --- a/src/util/netif-mgmt.h +++ b/src/util/netif-mgmt.h @@ -55,6 +55,8 @@ extern int netif_mgmt_remove_ipv6_route(int fd, const char* if_name, const uint8 extern int netif_mgmt_join_ipv6_multicast_address(int reqfd, const char* if_name, const uint8_t addr[16]); extern int netif_mgmt_leave_ipv6_multicast_address(int reqfd, const char* if_name, const uint8_t addr[16]); +extern int netif_mgmt_get_hw_address(int fd, const char* if_name, uint8_t hw_addr[6]); + #if defined(__cplusplus) } #endif diff --git a/src/wpantund/ICMP6RouterAdvertiser.cpp b/src/wpantund/ICMP6RouterAdvertiser.cpp new file mode 100644 index 00000000..754b8b70 --- /dev/null +++ b/src/wpantund/ICMP6RouterAdvertiser.cpp @@ -0,0 +1,230 @@ +/* + * + * Copyright (c) 2019 Google + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Description: + * Module sending ICMPv6 Router Advertisement. + * + */ + + +#if HAVE_CONFIG_H +#include +#endif + +#include "assert-macros.h" +#include +#include + +#include +#include +#include +#include +#include + +#include "netif-mgmt.h" +#include "NCPTypes.h" +#include "ICMP6RouterAdvertiser.h" +#include "NCPInstanceBase.h" + + +using namespace nl; +using namespace nl::wpantund; + +nl::wpantund::ICMP6RouterAdvertiser::ICMP6RouterAdvertiser(NCPInstanceBase* instance) + : mInstance(instance) + , mNetifMgmtFD(netif_mgmt_open()) + , mEnabled(false) + , mTxPeriod(DEFAULT_ROUTER_ADV_TX_PERIOD) + , mDefaultRoutePreference(0) + , mDefaultRouteLifetime(0) + , mStateChanged(false) +{ + mSocket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); +} + +nl::wpantund::ICMP6RouterAdvertiser::~ICMP6RouterAdvertiser(void) +{ + netif_mgmt_close(mNetifMgmtFD); + close(mSocket); +} + +void +nl::wpantund::ICMP6RouterAdvertiser::set_tx_period(uint32_t period) +{ + if (period > MAX_ROUTER_ADV_TX_PERIOD) { + mTxPeriod = MAX_ROUTER_ADV_TX_PERIOD; + } else if (period < MIN_ROUTER_ADV_TX_PERIOD) { + mTxPeriod = MIN_ROUTER_ADV_TX_PERIOD; + } else { + mTxPeriod = period; + } + + mStateChanged = true; +} + +void +nl::wpantund::ICMP6RouterAdvertiser::send_router_advert(const char *netif_name) +{ + enum { + ROUTE_INFO_OPTION_TYPE = 24, + ROUTE_INFO_OPTION_PRF_HIGH = (0x1 << 3), + ROUTE_INFO_OPTION_PRF_MEDIUM = (0x0 << 3), + ROUTE_INFO_OPTION_PRF_LOW = (0x3 << 3), + ROUTE_INFO_OPTION_LIFETIME = 3600, // in seconds + + HW_ADDRESS_SIZE = 6, + }; + + int ifindex; + uint8_t hw_addr[HW_ADDRESS_SIZE]; + Data msg; + struct sockaddr_in6 saddr; + struct nd_router_advert ra; + struct nd_opt_hdr opt_hdr; + struct + { + uint8_t mType; + uint8_t mLength; // (in units of 8 octets) + uint8_t mPrefixLength; + uint8_t mPreferenceFlags; + uint32_t mLifetime; + /* followed by the prefix (variable-length) */ + } opt_route_info; + std::map::iterator iter; + int num_route_info_opt = 0; + + if (mInstance->mInterfaceRoutes.empty() && (mDefaultRouteLifetime == 0)) { + goto bail; + } + + ifindex = netif_mgmt_get_ifindex(mNetifMgmtFD, netif_name); + + if (ifindex < 0) { + syslog(LOG_WARNING, "Could not find ifindex for netif \"%s\"", netif_name); + goto bail; + } + + if (netif_mgmt_get_hw_address(mNetifMgmtFD, netif_name, hw_addr) < 0) { + syslog(LOG_WARNING, "Could not get the hw address for netif \"%s\"", netif_name); + goto bail; + } + + // Prepare the dest IPv6 address ff02::1 + memset(&saddr, 0 , sizeof(saddr)); + saddr.sin6_family = AF_INET6; + saddr.sin6_addr.s6_addr[0] = 0xff; + saddr.sin6_addr.s6_addr[1] = 0x02; + saddr.sin6_addr.s6_addr[15] = 0x01; + saddr.sin6_scope_id = ifindex; + + // Prepare the Router Advertisement ICMP6 message header + ra.nd_ra_type = ND_ROUTER_ADVERT; + ra.nd_ra_code = 0; + ra.nd_ra_cksum = 0; + ra.nd_ra_curhoplimit = 255; + + if (mDefaultRoutePreference > 0) { + ra.nd_ra_flags_reserved = ROUTE_INFO_OPTION_PRF_HIGH; + } else if (mDefaultRoutePreference == 0) { + ra.nd_ra_flags_reserved = ROUTE_INFO_OPTION_PRF_MEDIUM; + } else { + ra.nd_ra_flags_reserved = ROUTE_INFO_OPTION_PRF_LOW; + } + + ra.nd_ra_router_lifetime = htons(mDefaultRouteLifetime); + ra.nd_ra_reachable = htonl(ROUTE_INFO_OPTION_LIFETIME); + ra.nd_ra_retransmit = htonl(0); + + msg.append(reinterpret_cast(&ra), sizeof(ra)); + + // Prepare the `Source Link-Layer Address` option + opt_hdr.nd_opt_type = ND_OPT_SOURCE_LINKADDR; + opt_hdr.nd_opt_len = 1; // in units of 8 octets + msg.append(reinterpret_cast(&opt_hdr), sizeof(opt_hdr)); + msg.append(hw_addr, sizeof(hw_addr)); + + // Prepare `Route Info` option for each interface route + for (iter = mInstance->mInterfaceRoutes.begin(); iter != mInstance->mInterfaceRoutes.end(); ++iter) { + uint8_t prefix_len = iter->first.get_length(); + uint32_t metric = iter->second.get_metric(); + + opt_route_info.mType = ROUTE_INFO_OPTION_TYPE; + + if (prefix_len > 64) { + opt_route_info.mLength = 3; + } else if (prefix_len > 0) { + opt_route_info.mLength = 2; + } else { + opt_route_info.mLength = 1; + } + + opt_route_info.mPrefixLength = prefix_len; + + // Note that smaller metric is higher preference. + if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricMedium) { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_HIGH; + } else if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricLow) { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_MEDIUM; + } else { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_LOW; + } + + opt_route_info.mLifetime = htonl(ROUTE_INFO_OPTION_LIFETIME); + + msg.append(reinterpret_cast(&opt_route_info), sizeof(opt_route_info)); + + if (prefix_len > 64) { + msg.append(iter->first.get_prefix().s6_addr, 16); + } else if (prefix_len > 0) { + msg.append(iter->first.get_prefix().s6_addr, 8); + } + + num_route_info_opt++; + } + + if (sendto(mSocket, msg.data(), msg.size(), 0, reinterpret_cast(&saddr), sizeof(saddr)) < 0) { + syslog(LOG_WARNING, "could not send router advert on netif \"%s\"", netif_name); + goto bail; + } + + syslog(LOG_INFO, "Sent ICMP6 RouterAdvert on \"%s\" (%d route info options)", netif_name, num_route_info_opt); + +bail: + return; +} + +int +nl::wpantund::ICMP6RouterAdvertiser::vprocess_event(int event, va_list args) +{ + EH_BEGIN(); + + while (true) { + EH_WAIT_UNTIL(ncp_state_is_associated(mInstance->get_ncp_state())); + + EH_WAIT_UNTIL_WITH_TIMEOUT(mTxPeriod, mStateChanged); + mStateChanged = false; + + if (mEnabled) { + for (std::set::iterator iter = mNetifs.begin(); iter != mNetifs.end(); ++iter) { + send_router_advert(iter->c_str()); + } + } + } + + EH_END(); +} + diff --git a/src/wpantund/ICMP6RouterAdvertiser.h b/src/wpantund/ICMP6RouterAdvertiser.h new file mode 100644 index 00000000..98b00f62 --- /dev/null +++ b/src/wpantund/ICMP6RouterAdvertiser.h @@ -0,0 +1,86 @@ +/* + * + * Copyright (c) 2019 Google. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Description: + * Module sending ICMPv6 Router Advertisement. + * + */ + +#ifndef __wpantund__ICMP6RouterAdvetiser__ +#define __wpantund__ICMP6RouterAdvetiser__ + +#include +#include + +#include "EventHandler.h" + +namespace nl { +namespace wpantund { + +class NCPInstanceBase; + +class ICMP6RouterAdvertiser : public EventHandler +{ +public: + enum { + MIN_ROUTER_ADV_TX_PERIOD = 4, // in seconds + MAX_ROUTER_ADV_TX_PERIOD = 1800, // in seconds + DEFAULT_ROUTER_ADV_TX_PERIOD = 10, // in seconds + }; + + ICMP6RouterAdvertiser(NCPInstanceBase* instance); + ~ICMP6RouterAdvertiser(void); + + void set_enabled(bool enabled) { mEnabled = enabled; mStateChanged = true; } + bool is_enabled(void) const { return mEnabled; } + + const std::set &get_netifs(void) const { return mNetifs; } + void add_netif(const std::string &netif) { mNetifs.insert(netif); mStateChanged = true;} + void remove_netif(const std::string &netif) { mNetifs.erase(netif); } + void clear_netifs(void) { mNetifs.clear(); } + + // RA period in seconds + void set_tx_period(uint32_t period); + uint32_t get_tx_period(void) const { return mTxPeriod; } + + void set_default_route_preference(int prf) { mDefaultRoutePreference = prf; mStateChanged = true; } + int get_default_route_preference(void) const { return mDefaultRoutePreference; } + + void set_default_route_lifetime(uint16_t lifetime) { mDefaultRouteLifetime = lifetime; mStateChanged = true; } + uint16_t get_default_route_lifetime(void) const { return mDefaultRouteLifetime; } + + void signal_routes_changed(void) { mStateChanged = true; } + + virtual int vprocess_event(int event, va_list args); +private: + void send_router_advert(const char *netif_name); + + NCPInstanceBase *mInstance; + int mSocket; + int mNetifMgmtFD; + bool mEnabled; + std::set mNetifs; + uint32_t mTxPeriod; + int mDefaultRoutePreference; + uint16_t mDefaultRouteLifetime; + bool mStateChanged; +}; + +}; // namespace wpantund +}; // namespace nl + +#endif /* defined(__wpantund__ICMP6RouterAdvetiser__) */ diff --git a/src/wpantund/Makefile.am b/src/wpantund/Makefile.am index 057e7465..71b07321 100644 --- a/src/wpantund/Makefile.am +++ b/src/wpantund/Makefile.am @@ -64,6 +64,8 @@ SOURCES = \ NCPConstants.h \ FirmwareUpgrade.h \ FirmwareUpgrade.cpp \ + ICMP6RouterAdvertiser.h \ + ICMP6RouterAdvertiser.cpp \ StatCollector.h \ StatCollector.cpp \ RunawayResetBackoffManager.cpp \ diff --git a/src/wpantund/NCPInstanceBase-Addresses.cpp b/src/wpantund/NCPInstanceBase-Addresses.cpp index 239ccd9c..0fa306b3 100644 --- a/src/wpantund/NCPInstanceBase-Addresses.cpp +++ b/src/wpantund/NCPInstanceBase-Addresses.cpp @@ -1209,6 +1209,7 @@ NCPInstanceBase::refresh_routes_on_interface(void) // new updated `mInterfaceRoutes` list. did_remove = true; + mICMP6RouterAdvertiser.signal_routes_changed(); break; } } @@ -1230,6 +1231,7 @@ NCPInstanceBase::refresh_routes_on_interface(void) syslog(LOG_INFO, "InterfaceRoutes: Adding %s", iter->second.get_description(iter->first).c_str()); mPrimaryInterface->add_route(&iter->first.get_prefix(), iter->first.get_length(), metric); mInterfaceRoutes[iter->first] = InterfaceRouteEntry(metric); + mICMP6RouterAdvertiser.signal_routes_changed(); } } } @@ -1248,6 +1250,7 @@ NCPInstanceBase::refresh_routes_on_interface(void) iter->second.get_description(iter->first).c_str()); mPrimaryInterface->add_route(&iter->first.get_prefix(), iter->first.get_length(), metric); mInterfaceRoutes[iter->first] = InterfaceRouteEntry(metric); + mICMP6RouterAdvertiser.signal_routes_changed(); } } } diff --git a/src/wpantund/NCPInstanceBase-AsyncIO.cpp b/src/wpantund/NCPInstanceBase-AsyncIO.cpp index 9f4d104e..dea73238 100644 --- a/src/wpantund/NCPInstanceBase-AsyncIO.cpp +++ b/src/wpantund/NCPInstanceBase-AsyncIO.cpp @@ -136,7 +136,8 @@ NCPInstanceBase::set_socket_adapter(const boost::shared_ptr &adap cms_t NCPInstanceBase::get_ms_to_next_event(void) { - cms_t ret(EventHandler::get_ms_to_next_event()); + cms_t ret = EventHandler::get_ms_to_next_event(); + cms_t router_adv_cms = mICMP6RouterAdvertiser.get_ms_to_next_event(); mSerialAdapter->update_fd_set(NULL, NULL, NULL, NULL, &ret); mPrimaryInterface->update_fd_set(NULL, NULL, NULL, NULL, &ret); @@ -153,6 +154,10 @@ NCPInstanceBase::get_ms_to_next_event(void) } } + if (ret > router_adv_cms) { + ret = router_adv_cms; + } + if (mRequestRouteRefresh) { ret = 0; } @@ -258,3 +263,14 @@ NCPInstanceBase::process(void) signal_fatal_error(ret); return; } + +int +NCPInstanceBase::vprocess_event(int event, va_list args) +{ + va_list tmp; + va_copy(tmp, args); + mICMP6RouterAdvertiser.vprocess_event(event, tmp); + va_end(tmp); + + return 0; +} \ No newline at end of file diff --git a/src/wpantund/NCPInstanceBase.cpp b/src/wpantund/NCPInstanceBase.cpp index bb342d31..0677f7d9 100644 --- a/src/wpantund/NCPInstanceBase.cpp +++ b/src/wpantund/NCPInstanceBase.cpp @@ -43,7 +43,8 @@ using namespace wpantund; NCPInstanceBase::NCPInstanceBase(const Settings& settings): mCommissioningRule(), - mCommissioningExpiration(0) + mCommissioningExpiration(0), + mICMP6RouterAdvertiser(this) { std::string wpan_interface_name = "wpan0"; @@ -330,6 +331,12 @@ NCPInstanceBase::get_supported_property_keys(void) const properties.insert(kWPANTUNDProperty_NestLabs_NetworkPassthruPort); + properties.insert(kWPANTUNDProperty_RouterAdvertEnable); + properties.insert(kWPANTUNDProperty_RouterAdvertNetifs); + properties.insert(kWPANTUNDProperty_RouterAdvertTxPeriod); + properties.insert(kWPANTUNDProperty_RouterAdvertDefaultRoutePreference); + properties.insert(kWPANTUNDProperty_RouterAdvertDefaultRouteLifetime); + return properties; } @@ -406,6 +413,11 @@ NCPInstanceBase::regsiter_all_get_handlers(void) REGISTER_GET_HANDLER(IPv6MulticastAddresses); REGISTER_GET_HANDLER(IPv6InterfaceRoutes); REGISTER_GET_HANDLER(DaemonSyslogMask); + REGISTER_GET_HANDLER(RouterAdvertEnable); + REGISTER_GET_HANDLER(RouterAdvertNetifs); + REGISTER_GET_HANDLER(RouterAdvertTxPeriod); + REGISTER_GET_HANDLER(RouterAdvertDefaultRoutePreference); + REGISTER_GET_HANDLER(RouterAdvertDefaultRouteLifetime); #undef REGISTER_GET_HANDLER } @@ -819,6 +831,36 @@ NCPInstanceBase::get_prop_DaemonSyslogMask(CallbackWithStatusArg1 cb) cb(kWPANTUNDStatus_Ok, mask_string); } +void +NCPInstanceBase::get_prop_RouterAdvertEnable(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.is_enabled())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertNetifs(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_netifs())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertTxPeriod(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_tx_period())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertDefaultRoutePreference(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_default_route_preference())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertDefaultRouteLifetime(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_default_route_lifetime())); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Set Handlers @@ -854,6 +896,11 @@ NCPInstanceBase::regsiter_all_set_handlers(void) REGISTER_SET_HANDLER(IPv6MeshLocalAddress); REGISTER_SET_HANDLER(DaemonAutoDeepSleep); REGISTER_SET_HANDLER(DaemonSyslogMask); + REGISTER_SET_HANDLER(RouterAdvertEnable); + REGISTER_SET_HANDLER(RouterAdvertNetifs); + REGISTER_SET_HANDLER(RouterAdvertTxPeriod); + REGISTER_SET_HANDLER(RouterAdvertDefaultRoutePreference); + REGISTER_SET_HANDLER(RouterAdvertDefaultRouteLifetime); #undef REGISTER_SET_HANDLER } @@ -1091,6 +1138,78 @@ NCPInstanceBase::set_prop_DaemonSyslogMask(const boost::any &value, CallbackWith } +void +NCPInstanceBase::set_prop_RouterAdvertEnable(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.set_enabled(any_to_bool(value)); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb) +{ + std::string names_str = any_to_string(value); + char names[500]; + char *p, *start; + bool did_end = false; + + mICMP6RouterAdvertiser.clear_netifs(); + + if (names_str.size() < sizeof(names) - 1) { + memcpy(names, names_str.c_str(), names_str.size()); + names[names_str.size()] = '\0'; + } else { + memcpy(names, names_str.c_str(), sizeof(names) - 1); + names[sizeof(names) - 1] = '\0'; + } + + p = names; + + do { + while ((*p != '\0') && (*p == ' ')) { + p++; + } + + start = p; + + while ((*p != '\0') && (*p != ' ')) { + p++; + } + + did_end = (*p == '\0'); + + if (start != p) { + *p = '\0'; + mICMP6RouterAdvertiser.add_netif(std::string(start)); + p++; + } + } while (!did_end); + +bail: + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertTxPeriod(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.set_tx_period(any_to_int(value)); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertDefaultRoutePreference(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.set_default_route_preference(any_to_int(value)); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertDefaultRouteLifetime(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.set_default_route_lifetime(any_to_int(value)); + cb(kWPANTUNDStatus_Ok); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Insert Handlers @@ -1111,6 +1230,7 @@ NCPInstanceBase::regsiter_all_insert_handlers(void) boost::bind(&NCPInstanceBase::insert_prop_##name, this, _1, _2)) REGISTER_INSERT_HANDLER(IPv6MulticastAddresses); + REGISTER_INSERT_HANDLER(RouterAdvertNetifs); #undef REGISTER_INSERT_HANDLER } @@ -1151,6 +1271,13 @@ NCPInstanceBase::insert_prop_IPv6MulticastAddresses(const boost::any &value, Cal multicast_address_was_joined(kOriginUser, address, cb); } +void +NCPInstanceBase::insert_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.add_netif(any_to_string(value)); + cb(kWPANTUNDStatus_Ok); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Remove Handlers @@ -1171,6 +1298,7 @@ NCPInstanceBase::regsiter_all_remove_handlers(void) boost::bind(&NCPInstanceBase::remove_prop_##name, this, _1, _2)) REGISTER_REMOVE_HANDLER(IPv6MulticastAddresses); + REGISTER_REMOVE_HANDLER(RouterAdvertNetifs); #undef REGISTER_REMOVE_HANDLER } @@ -1210,6 +1338,13 @@ NCPInstanceBase::remove_prop_IPv6MulticastAddresses(const boost::any &value, Cal multicast_address_was_left(kOriginUser, address, cb); } +void +NCPInstanceBase::remove_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.remove_netif(any_to_string(value)); + cb(kWPANTUNDStatus_Ok); +} + void NCPInstanceBase::signal_property_changed( const std::string& key, diff --git a/src/wpantund/NCPInstanceBase.h b/src/wpantund/NCPInstanceBase.h index 57a9bbd5..ce43eea1 100644 --- a/src/wpantund/NCPInstanceBase.h +++ b/src/wpantund/NCPInstanceBase.h @@ -30,12 +30,14 @@ #include "StatCollector.h" #include "NetworkRetain.h" #include "RunawayResetBackoffManager.h" +#include "ICMP6RouterAdvertiser.h" #include "Pcap.h" namespace nl { namespace wpantund { class NCPInstanceBase : public NCPInstance, public EventHandler { + friend class ICMP6RouterAdvertiser; public: enum { @@ -66,6 +68,7 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { // MARK: ASync I/O virtual cms_t get_ms_to_next_event(void); + virtual int vprocess_event(int event, va_list args); virtual int update_fd_set( fd_set *read_fd_set, @@ -364,6 +367,11 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { void get_prop_IPv6MulticastAddresses(CallbackWithStatusArg1 cb); void get_prop_IPv6InterfaceRoutes(CallbackWithStatusArg1 cb); void get_prop_DaemonSyslogMask(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertEnable(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertNetifs(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertTxPeriod(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertDefaultRoutePreference(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertDefaultRouteLifetime(CallbackWithStatusArg1 cb); void regsiter_all_set_handlers(void); @@ -385,14 +393,21 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { void set_prop_IPv6MeshLocalAddress(const boost::any &value, CallbackWithStatus cb); void set_prop_DaemonAutoDeepSleep(const boost::any &value, CallbackWithStatus cb); void set_prop_DaemonSyslogMask(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertEnable(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertTxPeriod(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertDefaultRoutePreference(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertDefaultRouteLifetime(const boost::any &value, CallbackWithStatus cb); void regsiter_all_insert_handlers(void); void insert_prop_IPv6MulticastAddresses(const boost::any &value, CallbackWithStatus cb); + void insert_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb); void regsiter_all_remove_handlers(void); void remove_prop_IPv6MulticastAddresses(const boost::any &value, CallbackWithStatus cb); + void remove_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb); private: @@ -776,6 +791,8 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { RunawayResetBackoffManager mRunawayResetBackoffManager; + ICMP6RouterAdvertiser mICMP6RouterAdvertiser; + protected: // ======================================================================== // MARK: Legacy Interface Support diff --git a/src/wpantund/wpan-properties.h b/src/wpantund/wpan-properties.h index 5cc099aa..c49542e3 100644 --- a/src/wpantund/wpan-properties.h +++ b/src/wpantund/wpan-properties.h @@ -339,6 +339,12 @@ #define kWPANTUNDProperty_ThreadLeaderServices "Thread:Leader:Services" #define kWPANTUNDProperty_ThreadLeaderServicesAsValMap "Thread:Leader:Services:AsValMap" +#define kWPANTUNDProperty_RouterAdvertEnable "RouterAdvert:Enable" +#define kWPANTUNDProperty_RouterAdvertNetifs "RouterAdvert:Netifs" +#define kWPANTUNDProperty_RouterAdvertTxPeriod "RouterAdvert:TxPeriod" +#define kWPANTUNDProperty_RouterAdvertDefaultRoutePreference "RouterAdvert:DefaultRoute:Preference" +#define kWPANTUNDProperty_RouterAdvertDefaultRouteLifetime "RouterAdvert:DefaultRoute:Lifetime" + // ---------------------------------------------------------------------------- #define kWPANTUNDNodeType_Unknown "unknown" From 3aea8bd65437c7bb0abb2b9f8eccab6933ff82cd Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 15 Oct 2019 13:54:18 -0700 Subject: [PATCH 2/3] (to squash) set the hop limit to 255 --- doc/router-advert-feature-guide.md | 14 +++++++------- src/wpantund/ICMP6RouterAdvertiser.cpp | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/router-advert-feature-guide.md b/doc/router-advert-feature-guide.md index c48d099d..ddffdfeb 100644 --- a/doc/router-advert-feature-guide.md +++ b/doc/router-advert-feature-guide.md @@ -45,7 +45,7 @@ Output of `tcpdump` on the interface shows the RA message: sudo tcpdump -n -i wpan1 icmp6 -vv -X - 11:01:58.991522 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 40) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 + 11:01:58.991522 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 40) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s source link-address option (1), length 8 (1): 00:00:00:00:00:00 0x0000: 0000 0000 0000 @@ -70,7 +70,7 @@ We can add or remove interface names to `RouterAdvert:Netifs` property. The RA m sudo tcpdump -n -i eno1 icmp6 -vv -X - 11:22:23.785865 IP6 (flowlabel 0xbcd66, hlim 1, next-header ICMPv6 (58) payload length: 40) fe80::6d87:d70b:c949:762a > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 + 11:22:23.785865 IP6 (flowlabel 0xbcd66, hlim 255, next-header ICMPv6 (58) payload length: 40) fe80::6d87:d70b:c949:762a > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 40 hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s source link-address option (1), length 8 (1): ec:b1:d7:2f:7c:b9 0x0000: ecb1 d72f 7cb9 @@ -114,7 +114,7 @@ When a route is added/removed (state changes), a new RA is sent immediately. `tcpdump` output now contains two route info options and is sent every 4 seconds: - 11:07:04.441659 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + 11:07:04.441659 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s source link-address option (1), length 8 (1): 00:00:00:00:00:00 0x0000: 0000 0000 0000 @@ -129,7 +129,7 @@ When a route is added/removed (state changes), a new RA is sent immediately. 0x0040: 1802 3000 0000 0e10 fd00 baba cafe 0000 ..0............. 0x0050: 1802 4000 0000 0e10 fd00 1234 0000 0000 ..@........4.... - 11:07:08.445622 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + 11:07:08.445622 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s source link-address option (1), length 8 (1): 00:00:00:00:00:00 0x0000: 0000 0000 0000 @@ -149,7 +149,7 @@ The property `RouterAdvert:DefaultRoute:Lifetime` specifies the lifetime value i wpanctl:wpan1> set RouterAdvert:DefaultRoute:Lifetime 1000 - 11:10:05.929627 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + 11:10:05.929627 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref medium, router lifetime 1000s, reachable time 3600s, retrans time 0s source link-address option (1), length 8 (1): 00:00:00:00:00:00 0x0000: 0000 0000 0000 @@ -158,12 +158,12 @@ The property `RouterAdvert:DefaultRoute:Preference` determines default route pre wpanctl:wpan1> set RouterAdvert:DefaultRoute:Preference 1 - 11:12:19.989599 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + 11:12:19.989599 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref high, router lifetime 1000s, reachable time 3600s, retrans time 0s wpanctl:wpan1> set RouterAdvert:DefaultRoute:Preference -v -1 - 11:13:08.078489 IP6 (flowlabel 0xba663, hlim 1, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + 11:13:08.078489 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref low, router lifetime 1000s, reachable time 3600s, retrans time 0s Limitations: diff --git a/src/wpantund/ICMP6RouterAdvertiser.cpp b/src/wpantund/ICMP6RouterAdvertiser.cpp index 754b8b70..cf92e492 100644 --- a/src/wpantund/ICMP6RouterAdvertiser.cpp +++ b/src/wpantund/ICMP6RouterAdvertiser.cpp @@ -54,6 +54,14 @@ nl::wpantund::ICMP6RouterAdvertiser::ICMP6RouterAdvertiser(NCPInstanceBase* inst , mStateChanged(false) { mSocket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + + if (mSocket >= 0) { + int hop_limit = 255; + + if (setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hop_limit, sizeof(hop_limit)) < 0) { + syslog(LOG_WARNING, "Failed to set multicast hops on socket"); + } + } } nl::wpantund::ICMP6RouterAdvertiser::~ICMP6RouterAdvertiser(void) From 1e6ea72b1a4e5da97d1cd87d211ff5eda9001638 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 16 Oct 2019 13:51:20 -0700 Subject: [PATCH 3/3] (to squash) allow RA prefix info (multiple prefixes) This commit adds prefix info option to RA (multiple prefixes) Also adds a property to allow user to disable "Router Info" option in RA. --- doc/router-advert-feature-guide.md | 175 +++++++++++++++++++- src/wpantund/ICMP6RouterAdvertiser.cpp | 178 +++++++++++++++++---- src/wpantund/ICMP6RouterAdvertiser.h | 27 ++++ src/wpantund/NCPInstanceBase-Addresses.cpp | 1 + src/wpantund/NCPInstanceBase.cpp | 159 ++++++++++++++++++ src/wpantund/NCPInstanceBase.h | 21 +++ src/wpantund/wpan-properties.h | 8 + 7 files changed, 535 insertions(+), 34 deletions(-) diff --git a/doc/router-advert-feature-guide.md b/doc/router-advert-feature-guide.md index ddffdfeb..5770338a 100644 --- a/doc/router-advert-feature-guide.md +++ b/doc/router-advert-feature-guide.md @@ -2,7 +2,9 @@ The `"Router Advertisement"` feature enables `wpantund` to emit periodic Neighbor Discovery ICMPv6 Router Advertisement (RA) messages announcing routes on other network interfaces related to Thread network routes. -The routes included in RA message mirror all the routes added on the host primary interface corresponding to the Thread network: +The emitted RA can inlcude a set of prefixes (determined by user) or annoucne a default route. + +When "Route Info" option is enabled, the routes included in RA message mirror all the routes added on the host primary interface corresponding to the Thread network: - Host routes associated with off-mesh routes within the Thread network (when `Daemon:OffMeshRoute:AutoAddOnInterface` feature is enabled). - Host routes associated with on-mesh prefixes within the Thread network (when `Daemon:OnMeshPrefix:AutoAddAsInterfaceRoute` feature is enabled). - The list of interface routes is available form wpan property `IPv6:Routes`. @@ -14,6 +16,16 @@ The wpantund RA feature can be enabled through property `RouterAdvert:Enable` (b - `RouterAdvert:TxPeriod` the tx period of RA messages in units of seconds. Minimum period is 4 seconds, max period is 1800 seconds. The period is set to min or max if the value being set is out of the supported range. On start it is set to 10 seconds. - `RouterAdvert:DefaultRoute:Lifetime` specifies the lifetime value in RA header (non-zero indicates that we are a default route). By default it is set to zero (i.e. not a default route). - `RouterAdvert:DefaultRoute:Preference` specifies the default route preference. Positive value indicates high, zero indicates medium, and negative indicates low preference. Default value is zero (medium). +- `RouterAdvert:AddRouteInfoOption` can be used to enable or disable adding of "Route Info" option in RA. When set to false, the emitted RAs would not contain any "Route Info" options. By default it is enabled (set to true). +- `RouterAdvert:Prefixes` specifies the list of prefixes which are included in the RA message. This is a list-based property (we can set the entire list or use `add` or `remove` command to update the list item by item). When adding to the list, we can specify the length, valid and preferred lifetime, and associated flags (on-link and auto-config). A set of wpan properties are defined to help specify these. The value specified through these properties would apply to any next prefix added/removed to the list: + + - `RouterAdvert:Prefix:PrefixLength` in bits 0-128 - default is 64. + - `RouterAdvert:Prefix:ValidLifetime` in seconds - default is 3600. + - `RouterAdvert:Prefix:PreferredLifetime` in seconds - default is 3600. + - `RouterAdvert:Prefix:Flag:OnLink` boolean value for on-link flag - default is true. + - `RouterAdvert:Prefix:Flag:AutoConfig` boolean value for auto-config flag - default is true. + +When issuing a wpantund `leave` command, the list of prefixes and netifs for router advertisement will be cleared. # Example of behavior @@ -166,5 +178,166 @@ The property `RouterAdvert:DefaultRoute:Preference` determines default route pre 11:13:08.078489 IP6 (flowlabel 0xba663, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::4801:7e22:7895:a656 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 hop limit 255, Flags [none], pref low, router lifetime 1000s, reachable time 3600s, retrans time 0s + +The command below show an example of how to add a prefix to RA. + + wpanctl:wpan1> get RouterAdvert:Prefixes + RouterAdvert:Prefixes = [] + +We first set all related parameters using `RouterAdvert:Prefix:` properties: + + wpanctl:wpan1> set RouterAdvert:Prefix:ValidLifetime 3000 + wpanctl:wpan1> set RouterAdvert:Prefix:PreferredLifetime 5000 + wpanctl:wpan1> set RouterAdvert:Prefix:PrefixLength 64 + wpanctl:wpan1> set RouterAdvert:Prefix:Flag:OnLink false + wpanctl:wpan1> set RouterAdvert:Prefix:Flag:AutoConfig false + wpanctl:wpan1> add RouterAdvert:Prefixes fd00:7777:: + wpanctl:wpan1> + wpanctl:wpan1> get RouterAdvert:Prefixes + RouterAdvert:Prefixes = [ + "prefix: fd00:7777::/64, flags:[ ], valid lifetime:3000, preferred lifetime:5000" + ] + +tcpdump shows the prefix option: + + 20:57:26.624969 IP6 (flowlabel 0x034c9, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::70c8:17e:f25a:6733 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:7777::/64, Flags [none], valid time 3000s, pref. time 5000s + 0x0000: 4000 0000 0bb8 0000 1388 0000 0000 fd00 + 0x0010: 7777 0000 0000 0000 0000 0000 0000 + 0x0000: 6000 34c9 0038 3aff fe80 0000 0000 0000 `.4..8:......... + 0x0010: 70c8 017e f25a 6733 ff02 0000 0000 0000 p..~.Zg3........ + 0x0020: 0000 0000 0000 0001 8600 cb64 ff00 0000 ...........d.... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 0304 4000 0000 0bb8 0000 1388 0000 0000 ..@............. + 0x0050: fd00 7777 0000 0000 0000 0000 0000 0000 ..ww............ + +Adding a new prefix with a different set of parameters: + + wpanctl:wpan1> set RouterAdvert:Prefix:Flag:AutoConfig true + wpanctl:wpan1> set RouterAdvert:Prefix:Flag:OnLink true + wpanctl:wpan1> set RouterAdvert:Prefix:PreferredLifetime 2500 + wpanctl:wpan1> set RouterAdvert:Prefix:PreferredLifetime 3000 + wpanctl:wpan1> set RouterAdvert:Prefix:PrefixLength 48 + wpanctl:wpan1> add RouterAdvert:Prefixes fd00:4321:: + + wpanctl:wpan1> get RouterAdvert:Prefixes + RouterAdvert:Prefixes = [ + "prefix: fd00:7777::/64, flags:[ ], valid lifetime:3000, preferred lifetime:5000" + "prefix: fd00:4321::/48, flags:[ on-link auto ], valid lifetime:3000, preferred lifetime:3000" + ] + +tcpdump + + 20:59:56.041669 IP6 (flowlabel 0x034c9, hlim 255, next-header ICMPv6 (58) payload length: 88) fe80::70c8:17e:f25a:6733 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 88 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:7777::/64, Flags [none], valid time 3000s, pref. time 5000s + 0x0000: 4000 0000 0bb8 0000 1388 0000 0000 fd00 + 0x0010: 7777 0000 0000 0000 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:4321::/48, Flags [onlink, auto], valid time 3000s, pref. time 3000s + 0x0000: 30c0 0000 0bb8 0000 0bb8 0000 0000 fd00 + 0x0010: 4321 0000 0000 0000 0000 0000 0000 + 0x0000: 6000 34c9 0058 3aff fe80 0000 0000 0000 `.4..X:......... + 0x0010: 70c8 017e f25a 6733 ff02 0000 0000 0000 p..~.Zg3........ + 0x0020: 0000 0000 0000 0001 8600 3fee ff00 0000 ..........?..... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 0304 4000 0000 0bb8 0000 1388 0000 0000 ..@............. + 0x0050: fd00 7777 0000 0000 0000 0000 0000 0000 ..ww............ + 0x0060: 0304 30c0 0000 0bb8 0000 0bb8 0000 0000 ..0............. + 0x0070: fd00 4321 0000 0000 0000 0000 0000 0000 ..C!............ + +Removing a prefix (when removing prefix only prefix length paramter is required along with the pregfix itself) + + wpanctl:wpan1> set RouterAdvert:Prefix:PrefixLength 48 + wpanctl:wpan1> remove RouterAdvert:Prefixes fd00:4321:: + wpanctl:wpan1> get RouterAdvert:Prefixes + RouterAdvert:Prefixes = [ + "prefix: fd00:7777::/64, flags:[ ], valid lifetime:3000, preferred lifetime:5000" + ] + +tcpdump + + 21:25:43.421691 IP6 (flowlabel 0x034c9, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::70c8:17e:f25a:6733 > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:7777::/64, Flags [none], valid time 3000s, pref. time 5000s + 0x0000: 4000 0000 0bb8 0000 1388 0000 0000 fd00 + 0x0010: 7777 0000 0000 0000 0000 0000 0000 + 0x0000: 6000 34c9 0038 3aff fe80 0000 0000 0000 `.4..8:......... + 0x0010: 70c8 017e f25a 6733 ff02 0000 0000 0000 p..~.Zg3........ + 0x0020: 0000 0000 0000 0001 8600 cb64 ff00 0000 ...........d.... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 0304 4000 0000 0bb8 0000 1388 0000 0000 ..@............. + 0x0050: fd00 7777 0000 0000 0000 0000 0000 0000 ..ww............ + +The `RouterAdvert:AddRouteInfoOption` can be used to disable adding of ant "Route Info" option to RA: + + # Add a prefix to RA and also a Thread on-mesh prefix (which will add a corresponding route on host): + wpanctl:wpan1> set RouterAdvert:Netifs wpan1 + wpanctl:wpan1> set RouterAdvert:Prefixes fd00:cafe:beef:: + wpanctl:wpan1> get RouterAdvert:Prefixes + RouterAdvert:Prefixes = [ + "prefix: fd00:cafe:beef::/64, flags:[ on-link auto ], valid lifetime:3600, preferred lifetime:3600" + ] + wpanctl:wpan1> add-prefix fd00:abba:: -o -c + Successfully added prefix "fd00:abba::" len:64 stable:0 [on-mesh:1 def-route:0 config:1 dhcp:0 slaac:0 pref:0 prio:med] + + wpanctl:wpan1> get IPv6:Routes + IPv6:Routes = [ + "fd00:abba::/64 metric:256 " + ] + + wpanctl:wpan1> set RouterAdvert:Enable true + +tcpdump shows prefix info and two route option. + +(NOTE: Since we are seing RA with `fd00:cafe:beef::` on wpan1 interface with on-link and auto-config flags, linux itself added an address with this prefix on `wpan1` interface which in turn was pushed to NCP by wpantund and its prefix added to list of on-mesh prefixes within Thread network. This in turn caused it to be added as a router info option as well) + + + 21:33:00.653646 IP6 (flowlabel 0x3d2ab, hlim 255, next-header ICMPv6 (58) payload length: 88) fe80::a4ab:2dcb:6a20:ca2b > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 88 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:cafe:beef::/64, Flags [onlink, auto], valid time 3600s, pref. time 3600s + 0x0000: 40c0 0000 0e10 0000 0e10 0000 0000 fd00 + 0x0010: cafe beef 0000 0000 0000 0000 0000 + route info option (24), length 16 (2): fd00:abba::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 abba 0000 0000 + route info option (24), length 16 (2): fd00:cafe:beef::/64, pref=medium, lifetime=3600s + 0x0000: 4000 0000 0e10 fd00 cafe beef 0000 + 0x0000: 6003 d2ab 0058 3aff fe80 0000 0000 0000 `....X:......... + 0x0010: a4ab 2dcb 6a20 ca2b ff02 0000 0000 0000 ..-.j..+........ + 0x0020: 0000 0000 0000 0001 8600 846f ff00 0000 ...........o.... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 0304 40c0 0000 0e10 0000 0e10 0000 0000 ..@............. + 0x0050: fd00 cafe beef 0000 0000 0000 0000 0000 ................ + 0x0060: 1802 4000 0000 0e10 fd00 abba 0000 0000 ..@............. + 0x0070: 1802 4000 0000 0e10 fd00 cafe beef 0000 ..@............. + +Now setting `RouterAdvert:AddRouteInfoOption` to `false` removes all route info options: + + set RouterAdvert:AddRouteInfoOption false + + + 21:33:43.462431 IP6 (flowlabel 0x3d2ab, hlim 255, next-header ICMPv6 (58) payload length: 56) fe80::a4ab:2dcb:6a20:ca2b > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 56 + hop limit 255, Flags [none], pref medium, router lifetime 0s, reachable time 3600s, retrans time 0s + source link-address option (1), length 8 (1): 00:00:00:00:00:00 + 0x0000: 0000 0000 0000 + prefix info option (3), length 32 (4): fd00:cafe:beef::/64, Flags [onlink, auto], valid time 3600s, pref. time 3600s + 0x0000: 40c0 0000 0e10 0000 0e10 0000 0000 fd00 + 0x0010: cafe beef 0000 0000 0000 0000 0000 + 0x0000: 6003 d2ab 0038 3aff fe80 0000 0000 0000 `....8:......... + 0x0010: a4ab 2dcb 6a20 ca2b ff02 0000 0000 0000 ..-.j..+........ + 0x0020: 0000 0000 0000 0001 8600 805f ff00 0000 ..........._.... + 0x0030: 0000 0e10 0000 0000 0101 0000 0000 0000 ................ + 0x0040: 0304 40c0 0000 0e10 0000 0e10 0000 0000 ..@............. + 0x0050: fd00 cafe beef 0000 0000 0000 0000 0000 ................ + + Limitations: - The current implementation does not support replying to Router Solicitation messages. diff --git a/src/wpantund/ICMP6RouterAdvertiser.cpp b/src/wpantund/ICMP6RouterAdvertiser.cpp index cf92e492..36dfbd6b 100644 --- a/src/wpantund/ICMP6RouterAdvertiser.cpp +++ b/src/wpantund/ICMP6RouterAdvertiser.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -51,6 +50,7 @@ nl::wpantund::ICMP6RouterAdvertiser::ICMP6RouterAdvertiser(NCPInstanceBase* inst , mTxPeriod(DEFAULT_ROUTER_ADV_TX_PERIOD) , mDefaultRoutePreference(0) , mDefaultRouteLifetime(0) + , mShouldAddRouteInfoOption(true) , mStateChanged(false) { mSocket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); @@ -70,6 +70,13 @@ nl::wpantund::ICMP6RouterAdvertiser::~ICMP6RouterAdvertiser(void) close(mSocket); } +void +nl::wpantund::ICMP6RouterAdvertiser::clear(void) +{ + clear_netifs(); + clear_prefixes(); +} + void nl::wpantund::ICMP6RouterAdvertiser::set_tx_period(uint32_t period) { @@ -84,6 +91,77 @@ nl::wpantund::ICMP6RouterAdvertiser::set_tx_period(uint32_t period) mStateChanged = true; } +std::list +nl::wpantund::ICMP6RouterAdvertiser::get_prefix_list(void) const +{ + std::list prefix_list; + char c_string[300]; + + for (std::list::const_iterator it = mPrefixes.begin(); it != mPrefixes.end(); it++) { + snprintf(c_string, sizeof(c_string), "prefix: %s/%d, flags:[ %s%s], valid lifetime:%u, preferred lifetime:%u", + in6_addr_to_string(it->mPrefix).c_str(), it->mPrefixLength, + it->mFlagOnLink ? "on-link " : "", it->mFlagAutoAddressConfig ? "auto ": "", + it->mValidLifetime, it->mPreferredLifetime); + prefix_list.push_back(std::string(c_string)); + } + + return prefix_list; +} + +void +nl::wpantund::ICMP6RouterAdvertiser::add_prefix( + const struct in6_addr &prefix, + uint8_t prefix_len, + uint32_t valid_lifetime, + uint32_t preferred_lifetime, + bool flag_on_link, + bool flag_auto_config +) { + Prefix entry; + + remove_prefix(prefix, prefix_len); + + entry.mPrefix = prefix; + entry.mPrefixLength = prefix_len; + entry.mValidLifetime = valid_lifetime; + entry.mPreferredLifetime = preferred_lifetime; + entry.mFlagOnLink = flag_on_link; + entry.mFlagAutoAddressConfig = flag_auto_config; + + in6_addr_apply_mask(entry.mPrefix, entry.mPrefixLength); + + remove_prefix(entry.mPrefix, entry.mPrefixLength); + mPrefixes.push_back(entry); + + mStateChanged = true; +} + + +void +nl::wpantund::ICMP6RouterAdvertiser::remove_prefix(const struct in6_addr &prefix, uint8_t prefix_len) +{ + struct in6_addr masked_prefix = prefix; + + in6_addr_apply_mask(masked_prefix, prefix_len); + + for (std::list::iterator iter = mPrefixes.begin(); iter != mPrefixes.end(); iter++) { + if ((iter->mPrefixLength == prefix_len) && (iter->mPrefix == masked_prefix)) { + mPrefixes.erase(iter); + mStateChanged = true; + break; + } + } +} + +void +nl::wpantund::ICMP6RouterAdvertiser::clear_prefixes(void) +{ + if (!mPrefixes.empty()) { + mPrefixes.clear(); + mStateChanged = true; + } +} + void nl::wpantund::ICMP6RouterAdvertiser::send_router_advert(const char *netif_name) { @@ -114,8 +192,11 @@ nl::wpantund::ICMP6RouterAdvertiser::send_router_advert(const char *netif_name) } opt_route_info; std::map::iterator iter; int num_route_info_opt = 0; + int num_prefix_info_opt = 0; - if (mInstance->mInterfaceRoutes.empty() && (mDefaultRouteLifetime == 0)) { + if ((mDefaultRouteLifetime == 0) && mPrefixes.empty() && + (!mShouldAddRouteInfoOption || mInstance->mInterfaceRoutes.empty()) + ) { goto bail; } @@ -165,43 +246,73 @@ nl::wpantund::ICMP6RouterAdvertiser::send_router_advert(const char *netif_name) msg.append(reinterpret_cast(&opt_hdr), sizeof(opt_hdr)); msg.append(hw_addr, sizeof(hw_addr)); - // Prepare `Route Info` option for each interface route - for (iter = mInstance->mInterfaceRoutes.begin(); iter != mInstance->mInterfaceRoutes.end(); ++iter) { - uint8_t prefix_len = iter->first.get_length(); - uint32_t metric = iter->second.get_metric(); - - opt_route_info.mType = ROUTE_INFO_OPTION_TYPE; - - if (prefix_len > 64) { - opt_route_info.mLength = 3; - } else if (prefix_len > 0) { - opt_route_info.mLength = 2; - } else { - opt_route_info.mLength = 1; - } + // Prepare `Prefix Info` options + for (std::list::iterator it = mPrefixes.begin(); it != mPrefixes.end(); ++it) { + struct nd_opt_prefix_info opt_pi; - opt_route_info.mPrefixLength = prefix_len; + memset(&opt_pi, 0, sizeof(opt_pi)); - // Note that smaller metric is higher preference. - if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricMedium) { - opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_HIGH; - } else if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricLow) { - opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_MEDIUM; - } else { - opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_LOW; - } + opt_pi.nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; + opt_pi.nd_opt_pi_len = 4; // in units of 8 octets - opt_route_info.mLifetime = htonl(ROUTE_INFO_OPTION_LIFETIME); + opt_pi.nd_opt_pi_prefix_len = it->mPrefixLength; - msg.append(reinterpret_cast(&opt_route_info), sizeof(opt_route_info)); + if (it->mFlagOnLink) { + opt_pi.nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; + } - if (prefix_len > 64) { - msg.append(iter->first.get_prefix().s6_addr, 16); - } else if (prefix_len > 0) { - msg.append(iter->first.get_prefix().s6_addr, 8); + if (it->mFlagAutoAddressConfig) { + opt_pi.nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; } - num_route_info_opt++; + opt_pi.nd_opt_pi_valid_time = htonl(it->mValidLifetime); + opt_pi.nd_opt_pi_preferred_time = htonl(it->mPreferredLifetime); + memcpy(&opt_pi.nd_opt_pi_prefix, &it->mPrefix, sizeof(struct in6_addr)); + + msg.append(reinterpret_cast(&opt_pi), sizeof(opt_pi)); + + num_prefix_info_opt++; + } + + // Prepare `Route Info` option for each interface route + if (mShouldAddRouteInfoOption) { + for (iter = mInstance->mInterfaceRoutes.begin(); iter != mInstance->mInterfaceRoutes.end(); ++iter) { + uint8_t prefix_len = iter->first.get_length(); + uint32_t metric = iter->second.get_metric(); + + opt_route_info.mType = ROUTE_INFO_OPTION_TYPE; + + if (prefix_len > 64) { + opt_route_info.mLength = 3; + } else if (prefix_len > 0) { + opt_route_info.mLength = 2; + } else { + opt_route_info.mLength = 1; + } + + opt_route_info.mPrefixLength = prefix_len; + + // Note that smaller metric is higher preference. + if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricMedium) { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_HIGH; + } else if (metric < NCPInstanceBase::InterfaceRouteEntry::kRouteMetricLow) { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_MEDIUM; + } else { + opt_route_info.mPreferenceFlags = ROUTE_INFO_OPTION_PRF_LOW; + } + + opt_route_info.mLifetime = htonl(ROUTE_INFO_OPTION_LIFETIME); + + msg.append(reinterpret_cast(&opt_route_info), sizeof(opt_route_info)); + + if (prefix_len > 64) { + msg.append(iter->first.get_prefix().s6_addr, 16); + } else if (prefix_len > 0) { + msg.append(iter->first.get_prefix().s6_addr, 8); + } + + num_route_info_opt++; + } } if (sendto(mSocket, msg.data(), msg.size(), 0, reinterpret_cast(&saddr), sizeof(saddr)) < 0) { @@ -209,7 +320,8 @@ nl::wpantund::ICMP6RouterAdvertiser::send_router_advert(const char *netif_name) goto bail; } - syslog(LOG_INFO, "Sent ICMP6 RouterAdvert on \"%s\" (%d route info options)", netif_name, num_route_info_opt); + syslog(LOG_INFO, "Sent ICMP6 RouterAdvert on \"%s\" (options: %d prefix info, %d route info)", netif_name, + num_prefix_info_opt, num_route_info_opt); bail: return; diff --git a/src/wpantund/ICMP6RouterAdvertiser.h b/src/wpantund/ICMP6RouterAdvertiser.h index 98b00f62..2fed9dbc 100644 --- a/src/wpantund/ICMP6RouterAdvertiser.h +++ b/src/wpantund/ICMP6RouterAdvertiser.h @@ -23,9 +23,12 @@ #ifndef __wpantund__ICMP6RouterAdvetiser__ #define __wpantund__ICMP6RouterAdvetiser__ +#include #include #include +#include + #include "EventHandler.h" namespace nl { @@ -45,6 +48,8 @@ class ICMP6RouterAdvertiser : public EventHandler ICMP6RouterAdvertiser(NCPInstanceBase* instance); ~ICMP6RouterAdvertiser(void); + void clear(void); + void set_enabled(bool enabled) { mEnabled = enabled; mStateChanged = true; } bool is_enabled(void) const { return mEnabled; } @@ -63,10 +68,30 @@ class ICMP6RouterAdvertiser : public EventHandler void set_default_route_lifetime(uint16_t lifetime) { mDefaultRouteLifetime = lifetime; mStateChanged = true; } uint16_t get_default_route_lifetime(void) const { return mDefaultRouteLifetime; } + void set_should_add_route_info_option(bool enable) { mShouldAddRouteInfoOption = enable; mStateChanged = true; } + bool get_should_add_route_info_option(void) const { return mShouldAddRouteInfoOption; } + + std::list get_prefix_list(void) const; + + void add_prefix(const struct in6_addr &prefix, uint8_t prefix_len, uint32_t valid_lifetime, + uint32_t preferred_lifetime, bool flag_on_link, bool flag_auto_config); + void remove_prefix(const struct in6_addr &prefix, uint8_t prefix_len); + void clear_prefixes(void); + void signal_routes_changed(void) { mStateChanged = true; } virtual int vprocess_event(int event, va_list args); + private: + struct Prefix { + struct in6_addr mPrefix; + uint8_t mPrefixLength; + uint32_t mValidLifetime; + uint32_t mPreferredLifetime; + bool mFlagOnLink; + bool mFlagAutoAddressConfig; + }; + void send_router_advert(const char *netif_name); NCPInstanceBase *mInstance; @@ -77,7 +102,9 @@ class ICMP6RouterAdvertiser : public EventHandler uint32_t mTxPeriod; int mDefaultRoutePreference; uint16_t mDefaultRouteLifetime; + bool mShouldAddRouteInfoOption; bool mStateChanged; + std::list mPrefixes; }; }; // namespace wpantund diff --git a/src/wpantund/NCPInstanceBase-Addresses.cpp b/src/wpantund/NCPInstanceBase-Addresses.cpp index 0fa306b3..47ef0933 100644 --- a/src/wpantund/NCPInstanceBase-Addresses.cpp +++ b/src/wpantund/NCPInstanceBase-Addresses.cpp @@ -442,6 +442,7 @@ NCPInstanceBase::remove_all_address_prefix_route_entries(void) mOffMeshRoutes.clear(); mInterfaceRoutes.clear(); mServiceEntries.clear(); + mICMP6RouterAdvertiser.clear(); } void diff --git a/src/wpantund/NCPInstanceBase.cpp b/src/wpantund/NCPInstanceBase.cpp index 0677f7d9..ee59f276 100644 --- a/src/wpantund/NCPInstanceBase.cpp +++ b/src/wpantund/NCPInstanceBase.cpp @@ -87,6 +87,12 @@ NCPInstanceBase::NCPInstanceBase(const Settings& settings): mWasBusy = false; mNCPIsMisbehaving = false; + mRouterAdvertPrefixValidLifetime = 3600; + mRouterAdvertPrefixPreferredLifetime = 3600; + mRouterAdvertPrefixLength = 64; + mRouterAdvertPrefixFlagOnLink = true; + mRouterAdvertPrefixFlagAutoConfig = true; + regsiter_all_get_handlers(); regsiter_all_set_handlers(); regsiter_all_insert_handlers(); @@ -336,6 +342,13 @@ NCPInstanceBase::get_supported_property_keys(void) const properties.insert(kWPANTUNDProperty_RouterAdvertTxPeriod); properties.insert(kWPANTUNDProperty_RouterAdvertDefaultRoutePreference); properties.insert(kWPANTUNDProperty_RouterAdvertDefaultRouteLifetime); + properties.insert(kWPANTUNDProperty_RouterAdvertAddRouteInfoOption); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixes); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixValidLifetime); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixPreferredLifetime); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixPrefixLength); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixFlagOnLink); + properties.insert(kWPANTUNDProperty_RouterAdvertPrefixFlagAutoConfig); return properties; } @@ -418,6 +431,13 @@ NCPInstanceBase::regsiter_all_get_handlers(void) REGISTER_GET_HANDLER(RouterAdvertTxPeriod); REGISTER_GET_HANDLER(RouterAdvertDefaultRoutePreference); REGISTER_GET_HANDLER(RouterAdvertDefaultRouteLifetime); + REGISTER_GET_HANDLER(RouterAdvertAddRouteInfoOption); + REGISTER_GET_HANDLER(RouterAdvertPrefixes); + REGISTER_GET_HANDLER(RouterAdvertPrefixValidLifetime); + REGISTER_GET_HANDLER(RouterAdvertPrefixPreferredLifetime); + REGISTER_GET_HANDLER(RouterAdvertPrefixPrefixLength); + REGISTER_GET_HANDLER(RouterAdvertPrefixFlagOnLink); + REGISTER_GET_HANDLER(RouterAdvertPrefixFlagAutoConfig); #undef REGISTER_GET_HANDLER } @@ -861,6 +881,49 @@ NCPInstanceBase::get_prop_RouterAdvertDefaultRouteLifetime(CallbackWithStatusArg cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_default_route_lifetime())); } +void +NCPInstanceBase::get_prop_RouterAdvertAddRouteInfoOption(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_should_add_route_info_option())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixes(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mICMP6RouterAdvertiser.get_prefix_list())); +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixValidLifetime(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mRouterAdvertPrefixValidLifetime)); + +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixPreferredLifetime(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mRouterAdvertPrefixPreferredLifetime)); +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixPrefixLength(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(static_cast(mRouterAdvertPrefixLength))); +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixFlagOnLink(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mRouterAdvertPrefixFlagOnLink)); +} + +void +NCPInstanceBase::get_prop_RouterAdvertPrefixFlagAutoConfig(CallbackWithStatusArg1 cb) +{ + cb(kWPANTUNDStatus_Ok, boost::any(mRouterAdvertPrefixFlagAutoConfig)); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Set Handlers @@ -901,6 +964,13 @@ NCPInstanceBase::regsiter_all_set_handlers(void) REGISTER_SET_HANDLER(RouterAdvertTxPeriod); REGISTER_SET_HANDLER(RouterAdvertDefaultRoutePreference); REGISTER_SET_HANDLER(RouterAdvertDefaultRouteLifetime); + REGISTER_SET_HANDLER(RouterAdvertAddRouteInfoOption); + REGISTER_SET_HANDLER(RouterAdvertPrefixValidLifetime); + REGISTER_SET_HANDLER(RouterAdvertPrefixPreferredLifetime); + REGISTER_SET_HANDLER(RouterAdvertPrefixPrefixLength); + REGISTER_SET_HANDLER(RouterAdvertPrefixFlagOnLink); + REGISTER_SET_HANDLER(RouterAdvertPrefixFlagAutoConfig); + REGISTER_SET_HANDLER(RouterAdvertPrefixes); #undef REGISTER_SET_HANDLER } @@ -1210,6 +1280,77 @@ NCPInstanceBase::set_prop_RouterAdvertDefaultRouteLifetime(const boost::any &val cb(kWPANTUNDStatus_Ok); } +void +NCPInstanceBase::set_prop_RouterAdvertAddRouteInfoOption(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.set_should_add_route_info_option(any_to_bool(value)); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixValidLifetime(const boost::any &value, CallbackWithStatus cb) +{ + int lifetime = any_to_int(value); + + if (lifetime < 0) { + lifetime = 0; + } + + mRouterAdvertPrefixValidLifetime = static_cast(lifetime); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixPreferredLifetime(const boost::any &value, CallbackWithStatus cb) +{ + int lifetime = any_to_int(value); + + if (lifetime < 0) { + lifetime = 0; + } + + mRouterAdvertPrefixPreferredLifetime = static_cast(lifetime); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixPrefixLength(const boost::any &value, CallbackWithStatus cb) +{ + int plen = any_to_int(value); + + if (plen < 0) { + plen = 0; + } + + if (plen > 128) { + plen = 128; + } + + mRouterAdvertPrefixLength = static_cast(plen); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixFlagOnLink(const boost::any &value, CallbackWithStatus cb) +{ + mRouterAdvertPrefixFlagOnLink = any_to_bool(value); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixFlagAutoConfig(const boost::any &value, CallbackWithStatus cb) +{ + mRouterAdvertPrefixFlagAutoConfig = any_to_bool(value); + cb(kWPANTUNDStatus_Ok); +} + +void +NCPInstanceBase::set_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.clear_prefixes(); + insert_prop_RouterAdvertPrefixes(value, cb); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Insert Handlers @@ -1231,6 +1372,7 @@ NCPInstanceBase::regsiter_all_insert_handlers(void) REGISTER_INSERT_HANDLER(IPv6MulticastAddresses); REGISTER_INSERT_HANDLER(RouterAdvertNetifs); + REGISTER_INSERT_HANDLER(RouterAdvertPrefixes); #undef REGISTER_INSERT_HANDLER } @@ -1278,6 +1420,15 @@ NCPInstanceBase::insert_prop_RouterAdvertNetifs(const boost::any &value, Callbac cb(kWPANTUNDStatus_Ok); } +void +NCPInstanceBase::insert_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.add_prefix(any_to_ipv6(value), mRouterAdvertPrefixLength, mRouterAdvertPrefixValidLifetime, + mRouterAdvertPrefixPreferredLifetime, mRouterAdvertPrefixFlagOnLink, mRouterAdvertPrefixFlagAutoConfig); + + cb(kWPANTUNDStatus_Ok); +} + // ---------------------------------------------------------------------------- // MARK: - // MARK: Property Remove Handlers @@ -1299,6 +1450,7 @@ NCPInstanceBase::regsiter_all_remove_handlers(void) REGISTER_REMOVE_HANDLER(IPv6MulticastAddresses); REGISTER_REMOVE_HANDLER(RouterAdvertNetifs); + REGISTER_REMOVE_HANDLER(RouterAdvertPrefixes); #undef REGISTER_REMOVE_HANDLER } @@ -1345,6 +1497,13 @@ NCPInstanceBase::remove_prop_RouterAdvertNetifs(const boost::any &value, Callbac cb(kWPANTUNDStatus_Ok); } +void +NCPInstanceBase::remove_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb) +{ + mICMP6RouterAdvertiser.remove_prefix(any_to_ipv6(value), mRouterAdvertPrefixLength); + cb(kWPANTUNDStatus_Ok); +} + void NCPInstanceBase::signal_property_changed( const std::string& key, diff --git a/src/wpantund/NCPInstanceBase.h b/src/wpantund/NCPInstanceBase.h index ce43eea1..8c33db81 100644 --- a/src/wpantund/NCPInstanceBase.h +++ b/src/wpantund/NCPInstanceBase.h @@ -372,6 +372,13 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { void get_prop_RouterAdvertTxPeriod(CallbackWithStatusArg1 cb); void get_prop_RouterAdvertDefaultRoutePreference(CallbackWithStatusArg1 cb); void get_prop_RouterAdvertDefaultRouteLifetime(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertAddRouteInfoOption(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixes(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixValidLifetime(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixPreferredLifetime(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixPrefixLength(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixFlagOnLink(CallbackWithStatusArg1 cb); + void get_prop_RouterAdvertPrefixFlagAutoConfig(CallbackWithStatusArg1 cb); void regsiter_all_set_handlers(void); @@ -398,16 +405,25 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { void set_prop_RouterAdvertTxPeriod(const boost::any &value, CallbackWithStatus cb); void set_prop_RouterAdvertDefaultRoutePreference(const boost::any &value, CallbackWithStatus cb); void set_prop_RouterAdvertDefaultRouteLifetime(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertAddRouteInfoOption(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixValidLifetime(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixPreferredLifetime(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixPrefixLength(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixFlagOnLink(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixFlagAutoConfig(const boost::any &value, CallbackWithStatus cb); + void set_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb); void regsiter_all_insert_handlers(void); void insert_prop_IPv6MulticastAddresses(const boost::any &value, CallbackWithStatus cb); void insert_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb); + void insert_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb); void regsiter_all_remove_handlers(void); void remove_prop_IPv6MulticastAddresses(const boost::any &value, CallbackWithStatus cb); void remove_prop_RouterAdvertNetifs(const boost::any &value, CallbackWithStatus cb); + void remove_prop_RouterAdvertPrefixes(const boost::any &value, CallbackWithStatus cb); private: @@ -792,6 +808,11 @@ class NCPInstanceBase : public NCPInstance, public EventHandler { RunawayResetBackoffManager mRunawayResetBackoffManager; ICMP6RouterAdvertiser mICMP6RouterAdvertiser; + uint32_t mRouterAdvertPrefixValidLifetime; + uint32_t mRouterAdvertPrefixPreferredLifetime; + uint8_t mRouterAdvertPrefixLength; + bool mRouterAdvertPrefixFlagOnLink; + bool mRouterAdvertPrefixFlagAutoConfig; protected: // ======================================================================== diff --git a/src/wpantund/wpan-properties.h b/src/wpantund/wpan-properties.h index c49542e3..fb4792f2 100644 --- a/src/wpantund/wpan-properties.h +++ b/src/wpantund/wpan-properties.h @@ -344,6 +344,14 @@ #define kWPANTUNDProperty_RouterAdvertTxPeriod "RouterAdvert:TxPeriod" #define kWPANTUNDProperty_RouterAdvertDefaultRoutePreference "RouterAdvert:DefaultRoute:Preference" #define kWPANTUNDProperty_RouterAdvertDefaultRouteLifetime "RouterAdvert:DefaultRoute:Lifetime" +#define kWPANTUNDProperty_RouterAdvertAddRouteInfoOption "RouterAdvert:AddRouteInfoOption" +#define kWPANTUNDProperty_RouterAdvertPrefixes "RouterAdvert:Prefixes" +// Properties to configure parameters associated with any next added prefix +#define kWPANTUNDProperty_RouterAdvertPrefixValidLifetime "RouterAdvert:Prefix:ValidLifetime" +#define kWPANTUNDProperty_RouterAdvertPrefixPreferredLifetime "RouterAdvert:Prefix:PreferredLifetime" +#define kWPANTUNDProperty_RouterAdvertPrefixPrefixLength "RouterAdvert:Prefix:PrefixLength" +#define kWPANTUNDProperty_RouterAdvertPrefixFlagOnLink "RouterAdvert:Prefix:Flag:OnLink" +#define kWPANTUNDProperty_RouterAdvertPrefixFlagAutoConfig "RouterAdvert:Prefix:Flag:AutoConfig" // ----------------------------------------------------------------------------