blob: 29cb226ebd0c659e95ae67ffa44043de3cd2e3fb [file] [log] [blame]
diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c
index d4673ca6..7c2d0c45 100644
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -84,12 +84,19 @@
#if LWIP_ND6_NUM_ROUTERS > 127
#error LWIP_ND6_NUM_ROUTERS must fit into an s8_t (max value: 127)
#endif
+#if LWIP_ND6_SUPPORT_RIO && LWIP_ND6_NUM_ROUTES <= 0
+#error LWIP_ND6_NUM_ROUTES must be > 0 if LWIP_ND6_SUPPORT_RIO is on.
+#endif
+
/* Router tables. */
struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS];
struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS];
struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES];
struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS];
+#if LWIP_ND6_SUPPORT_RIO
+struct nd6_route_list_entry route_list[LWIP_ND6_NUM_ROUTES];
+#endif
/* Default values, can be updated by a RA message. */
u32_t reachable_time = LWIP_ND6_REACHABLE_TIME;
@@ -132,6 +139,10 @@ static s8_t nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
static s8_t nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif);
static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
+#if LWIP_ND6_SUPPORT_RIO
+static s8_t nd6_get_route(const ip6_addr_t *prefix, const u8_t prefix_len);
+static s8_t nd6_new_route(const ip6_addr_t *prefix, const u8_t prefix_len);
+#endif
#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
@@ -763,12 +774,60 @@ nd6_input(struct pbuf *p, struct netif *inp)
break;
}
- case ND6_OPTION_TYPE_ROUTE_INFO:
- /* @todo implement preferred routes.
- struct route_option * route_opt;
- route_opt = (struct route_option *)buffer;*/
-
+ case ND6_OPTION_TYPE_ROUTE_INFO: {
+#if LWIP_ND6_SUPPORT_RIO
+ struct route_option *route_opt;
+ uint32_t route_lifetime;
+ uint8_t prefix_length;
+ uint8_t preference;
+ ip6_addr_t prefix;
+ ip6_addr_p_t packed_prefix;
+ int idx;
+ size_t addr_bytes;
+ if (option_len < sizeof(struct route_option)) {
+ goto lenerr_drop_free_return;
+ }
+ route_opt = (struct route_option *)buffer;
+ memcpy(&prefix_length, &route_opt->prefix_length, sizeof(prefix_length));
+ memcpy(&preference, &route_opt->preference, sizeof(preference));
+ memcpy(&route_lifetime, &route_opt->route_lifetime, sizeof(route_lifetime));
+
+ /* The struct definition contains only the first byte of the address.
+ The option should have enough bytes to encode prefix_length bits
+ of the address. */
+ addr_bytes = (prefix_length + 7) >> 3;
+ if (addr_bytes > sizeof(ip6_addr_p_t) ||
+ option_len < sizeof(struct route_option) + addr_bytes - 1) {
+ goto lenerr_drop_free_return;
+ }
+ /* For now, we only consider 64-bit prefix lengths. */
+ if (prefix_length != 64) {
+ break;
+ }
+ memset(&packed_prefix, 0, sizeof(packed_prefix));
+ memcpy(&packed_prefix, route_opt->prefix, addr_bytes);
+ ip6_addr_copy_from_packed(prefix, packed_prefix);
+ /* Link-local, any address prefix and multicast should not have defined
+ routes set through RIO options. If we got one of these, disregard it. */
+ if (ip6_addr_isany(&prefix) || ip6_addr_islinklocal(&prefix) ||
+ ip6_addr_ismulticast(&prefix)) {
+ break;
+ }
+ route_lifetime = lwip_ntohl(route_lifetime);
+ idx = nd6_get_route(&prefix, prefix_length);
+ if (idx < 0 && route_lifetime > 0) {
+ /* Create a new cache entry */
+ idx = nd6_new_route(&prefix, prefix_length);
+ }
+ if (idx >= 0) {
+ route_list[idx].invalidation_timer = route_lifetime;
+ route_list[idx].preference = preference;
+ route_list[idx].router_list_entry_index = nd6_get_router(ip6_current_src_addr(), inp);
+ route_list[idx].netif = inp;
+ }
+#endif
break;
+ }
#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
case ND6_OPTION_TYPE_RDNSS:
{
@@ -1075,6 +1134,21 @@ nd6_tmr(void)
}
}
+#if LWIP_ND6_SUPPORT_RIO
+ /* Process route entries. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; ++i) {
+ if (ip6_addr_isany(&route_list[i].prefix)) {
+ continue;
+ }
+ if (route_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ ip6_addr_set_any(&route_list[i].prefix);
+ route_list[i].invalidation_timer = 0;
+ } else {
+ route_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+ }
+ }
+#endif
+
/* Process our own addresses, updating address lifetimes and/or DAD state. */
NETIF_FOREACH(netif) {
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
@@ -1677,6 +1751,16 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
return 1;
}
}
+
+#if LWIP_ND6_SUPPORT_RIO
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; i++) {
+ if ((route_list[i].netif == netif) &&
+ (route_list[i].invalidation_timer > 0) &&
+ ip6_addr_net_eq(ip6addr, &(route_list[i].prefix))) {
+ return 1;
+ }
+ }
+#endif
return 0;
}
@@ -1700,7 +1784,34 @@ nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
s8_t i, j, valid_router;
static s8_t last_router;
- LWIP_UNUSED_ARG(ip6addr); /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+#if LWIP_ND6_SUPPORT_RIO
+ /* Check first if we have a route explicitly set for this address from a
+ RIO. This works because we're only considering 64-bit prefixes in RIO
+ and the prefix option. If we later move to support non-64 prefixes,
+ these will need to generate candidate routers and rank based on prefix
+ length */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; ++i) {
+ /* For now, we assume all route prefixes are /64 */
+ if (ip6_addr_isany(&route_list[i].prefix)) {
+ continue;
+ }
+ if (ip6_addr_netcmp(&route_list[i].prefix, ip6addr)) {
+ s8_t router_idx = route_list[i].router_list_entry_index;
+ router_netif = default_router_list[router_idx].neighbor_entry->netif;
+ /* TODO: Implement preferences. */
+ if (netif != NULL && router_netif != netif) {
+ continue;
+ }
+ /* TODO: If we're up, but not reacheable, then what? */
+ if (default_router_list[router_idx].neighbor_entry->state !=
+ ND6_INCOMPLETE) {
+ return router_idx;
+ }
+ }
+ }
+#else
+ LWIP_UNUSED_ARG(ip6addr);
+#endif
/* @todo: implement default router preference */
@@ -1770,7 +1881,6 @@ nd6_find_route(const ip6_addr_t *ip6addr)
{
struct netif *netif;
s8_t i;
-
/* @todo decide if it makes sense to check the destination cache first */
/* Check if there is a matching on-link prefix. There may be multiple
@@ -1935,6 +2045,57 @@ nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
return -1;
}
+#if LWIP_ND6_SUPPORT_RIO
+/**
+ * Find the cached entry for an RIO configured route.
+ *
+ * @param prefix the IPv6 prefix for the route.
+ * @param prefix_len the length of the prefix (currently assumed /64)
+ * @return the index on the route table, or -1 if not found
+ */
+static s8_t
+nd6_get_route(const ip6_addr_t *prefix, const u8_t prefix_len)
+{
+ s8_t i;
+ /* TODO: add support for non- /64 prefixes */
+ LWIP_UNUSED_ARG(prefix_len);
+ /* Look for prefix in list. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; ++i) {
+ if (ip6_addr_netcmp(&route_list[i].prefix, prefix)) {
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+
+/**
+ * Creates a new entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix for the route.
+ * @param prefix_len the length of the prefix (currently assumed /64)
+ * @return the index on the prefix table, or -1 if not created
+ */
+static s8_t
+nd6_new_route(const ip6_addr_t *prefix, const u8_t prefix_len)
+{
+ s8_t i;
+
+ /* Create new entry. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; ++i) {
+ if (ip6_addr_isany(&route_list[i].prefix) || route_list[i].invalidation_timer == 0) {
+ ip6_addr_set(&(route_list[i].prefix), prefix);
+ route_list[i].prefix_len = prefix_len;
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+#endif
+
/**
* Determine the next hop for a destination. Will determine if the
* destination is on-link, else a suitable on-link router is selected.
@@ -2394,7 +2555,7 @@ nd6_reachability_hint(const ip6_addr_t *ip6addr)
#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
/**
- * Remove all prefix, neighbor_cache and router entries of the specified netif.
+ * Remove all prefix, neighbor_cachei, route and router entries of the specified netif.
*
* @param netif points to a network interface
*/
@@ -2420,6 +2581,18 @@ nd6_cleanup_netif(struct netif *netif)
nd6_free_neighbor_cache_entry(i);
}
}
+#if LWIP_ND6_SUPPORT_RIO
+ for (i = 0; i < LWIP_ND6_NUM_ROUTES; i++) {
+ if (route_list[i].netif == netif) {
+ /* Remove the whole route - if this netif is not up, this is invalid */
+ ip6_addr_set_any(&route_list[i].prefix);
+ route_list[i].prefix_len = 0;
+ route_list[i].invalidation_timer = 0;
+ route_list[i].netif = NULL;
+ }
+ }
+#endif /* LWIP_ND6_SUPPORT_RIO */
+
/* Clear the destination cache, since many entries may now have become
* invalid for one of several reasons. As destination cache entries have no
* netif association, use a sledgehammer approach (this can be improved). */
diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h
index 9fe0bf04..297c2019 100644
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -2625,6 +2625,20 @@
#define LWIP_ND6_NUM_PREFIXES 5
#endif
+/**
+ * LWIP_ND6_SUPPORT_RIO: Support route information options in nd6 packets.
+ */
+#if !defined LWIP_ND6_SUPPORT_RIO || defined __DOXYGEN__
+#define LWIP_ND6_SUPPORT_RIO LWIP_IPV6
+#endif
+
+/**
+ * LWIP_ND6_NUM_ROUTES: number of entries in IPv6 route list.
+ */
+#if !defined LWIP_ND6_NUM_ROUTES || defined __DOXYGEN__
+#define LWIP_ND6_NUM_ROUTES 5
+#endif
+
/**
* LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache
*/
diff --git a/src/include/lwip/priv/nd6_priv.h b/src/include/lwip/priv/nd6_priv.h
index 75d5f02d..6b4aea23 100644
--- a/src/include/lwip/priv/nd6_priv.h
+++ b/src/include/lwip/priv/nd6_priv.h
@@ -104,6 +104,18 @@ struct nd6_prefix_list_entry {
u32_t invalidation_timer; /* in seconds */
};
+struct nd6_route_list_entry {
+ ip6_addr_t prefix;
+ u8_t prefix_len;
+ u8_t preference;
+ /* Router messages with this prefix should be sent to
+ use the neighbour entry in the router to set the
+ next hop */
+ s8_t router_list_entry_index;
+ struct netif* netif;
+ u32_t invalidation_timer;
+};
+
struct nd6_router_list_entry {
struct nd6_neighbor_cache_entry *neighbor_entry;
u32_t invalidation_timer; /* in seconds */
@@ -129,6 +141,10 @@ extern struct nd6_neighbor_cache_entry neighbor_cache[];
extern struct nd6_destination_cache_entry destination_cache[];
extern struct nd6_prefix_list_entry prefix_list[];
extern struct nd6_router_list_entry default_router_list[];
+#if LWIP_ND6_SUPPORT_RIO
+extern struct nd6_route_list_entry route_list[];
+#endif
+
/* Default values, can be updated by a RA message. */
extern u32_t reachable_time;
diff --git a/src/include/lwip/prot/nd6.h b/src/include/lwip/prot/nd6.h
index c270d07c..e97d47a7 100644
--- a/src/include/lwip/prot/nd6.h
+++ b/src/include/lwip/prot/nd6.h
@@ -240,7 +240,9 @@ struct route_option {
PACK_STRUCT_FLD_8(u8_t prefix_length);
PACK_STRUCT_FLD_8(u8_t preference);
PACK_STRUCT_FIELD(u32_t route_lifetime);
- PACK_STRUCT_FLD_S(ip6_addr_p_t prefix);
+ /* The prefix is formatted like a packed address, but is of a variable length
+ large enough to contain prefix_length bits. See RFC 4191 section 2.3. */
+ PACK_STRUCT_FLD_S(u8_t prefix[1]);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES