Add support for the PREF64 option (RFC 8781), used in Router
Advertisements to communicate NAT64 prefixes to hosts.
Example:
$ ndptool -i eth0 monitor
NDP payload len 96, from addr: fe80::38e1:1bff:fe6a:e266, iface: eth0
Type: RA
...
Prefix: 2001:db8::/64, valid_time: 1800s, ...
NAT64 Prefix: 64:ff9b::/96, lifetime: 1800s
Signed-off-by: Beniamino Galvani <bgalvani(a)redhat.com>
---
include/ndp.h | 5 +++
libndp/libndp.c | 103 +++++++++++++++++++++++++++++++++++++++++++
libndp/ndp_private.h | 12 +++++
utils/ndptool.c | 15 +++++++
4 files changed, 135 insertions(+)
diff --git a/include/ndp.h b/include/ndp.h
index 7bf8794..c3e04df 100644
--- a/include/ndp.h
+++ b/include/ndp.h
@@ -124,6 +124,7 @@ enum ndp_msg_opt_type {
NDP_MSG_OPT_ROUTE, /* Route Information */
NDP_MSG_OPT_RDNSS, /* Recursive DNS Server */
NDP_MSG_OPT_DNSSL, /* DNS Search List */
+ NDP_MSG_OPT_PREF64, /* NAT64 prefix */
};
int ndp_msg_next_opt_offset(struct ndp_msg *msg, int offset,
@@ -159,6 +160,10 @@ uint32_t ndp_msg_opt_rdnss_lifetime(struct ndp_msg *msg, int offset);
struct in6_addr *ndp_msg_opt_rdnss_addr(struct ndp_msg *msg, int offset,
int addr_index);
+uint16_t ndp_msg_opt_pref64_lifetime(struct ndp_msg *msg, int offset);
+uint8_t ndp_msg_opt_pref64_prefix_length(struct ndp_msg *msg, int offset);
+struct in6_addr *ndp_msg_opt_pref64_prefix(struct ndp_msg *msg, int offset);
+
#define ndp_msg_opt_rdnss_for_each_addr(addr, addr_index, msg, offset) \
for (addr_index = 0, \
addr = ndp_msg_opt_rdnss_addr(msg, offset, addr_index); \
diff --git a/libndp/libndp.c b/libndp/libndp.c
index 72ec92e..860b785 100644
--- a/libndp/libndp.c
+++ b/libndp/libndp.c
@@ -1245,6 +1245,27 @@ static bool ndp_msg_opt_route_check_valid(void *opt_data)
return true;
}
+static bool ndp_msg_opt_pref64_check_valid(void *opt_data)
+{
+ struct __nd_opt_pref64 *pref64 = opt_data;
+ uint16_t plc;
+
+ /* rfc8781: the receiver MUST ignore the PREF64 option if the
+ * Length field value is not 2 */
+ if (pref64->nd_opt_pref64_len != 2)
+ return false;
+
+ /* The PLC field values 0, 1, 2, 3, 4, and 5 indicate the NAT64
+ * prefix length of 96, 64, 56, 48, 40, and 32 bits, respectively.
+ * The receiver MUST ignore the PREF64 option if the Prefix
+ * Length Code field is not set to one of those values.*/
+ plc = ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_PLC_MASK;
+ if (plc > 5)
+ return false;
+
+ return true;
+}
+
static struct ndp_msg_opt_type_info ndp_msg_opt_type_info_list[] =
{
[NDP_MSG_OPT_SLLADDR] = {
@@ -1277,6 +1298,11 @@ static struct ndp_msg_opt_type_info ndp_msg_opt_type_info_list[] =
.raw_type = __ND_OPT_DNSSL,
.raw_struct_size = sizeof(struct __nd_opt_dnssl),
},
+ [NDP_MSG_OPT_PREF64] = {
+ .raw_type = __ND_OPT_PREF64,
+ .raw_struct_size = sizeof(struct __nd_opt_pref64),
+ .check_valid = ndp_msg_opt_pref64_check_valid,
+ },
};
#define NDP_MSG_OPT_TYPE_LIST_SIZE ARRAY_SIZE(ndp_msg_opt_type_info_list)
@@ -1821,6 +1847,83 @@ char *ndp_msg_opt_dnssl_domain(struct ndp_msg *msg, int offset,
return NULL;
}
+/**
+ * ndp_msg_opt_pref64_lifetime:
+ * @msg: message structure
+ * @offset: in-message offset
+ *
+ * Get the NAT64 prefix lifetime.
+ * User should use this function only inside ndp_msg_opt_for_each_offset()
+ * macro loop.
+ *
+ * Returns: the NAT64 prefix lifetime in seconds.
+ **/
+NDP_EXPORT
+uint16_t ndp_msg_opt_pref64_lifetime(struct ndp_msg *msg, int offset)
+{
+ struct __nd_opt_pref64 *pref64 =
+ ndp_msg_payload_opts_offset(msg, offset);
+
+ return ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_LFT_MASK;
+}
+
+static uint8_t ndp_msg_opt_pref64_plen(struct __nd_opt_pref64 *pref64)
+{
+ uint8_t plc;
+
+ plc = ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_PLC_MASK;
+ if (plc == 0)
+ return 96;
+
+ return 72 - plc * 8;
+}
+
+/**
+ * ndp_msg_opt_pref64_prefix_length:
+ * @msg: message structure
+ * @offset: in-message offset
+ *
+ * Get the NAT64 prefix length.
+ * User should use this function only inside ndp_msg_opt_for_each_offset()
+ * macro loop.
+ *
+ * Returns: the NAT64 prefix length (32, 40, 48, 56, 64 or 96).
+ **/
+NDP_EXPORT
+uint8_t ndp_msg_opt_pref64_prefix_length(struct ndp_msg *msg, int offset)
+{
+ struct __nd_opt_pref64 *pref64 =
+ ndp_msg_payload_opts_offset(msg, offset);
+
+ return ndp_msg_opt_pref64_plen(pref64);
+}
+
+/**
+ * ndp_msg_opt_pref64_prefix:
+ * @msg: message structure
+ * @offset: in-message offset
+ *
+ * Get the NAT64 prefix.
+ * User should use this function only inside ndp_msg_opt_for_each_offset()
+ * macro loop.
+ *
+ * Returns: the NAT64 prefix.
+ **/
+NDP_EXPORT
+struct in6_addr *ndp_msg_opt_pref64_prefix(struct ndp_msg *msg, int offset)
+{
+ static NDP_THREAD struct in6_addr addr;
+ struct __nd_opt_pref64 *pref64 =
+ ndp_msg_payload_opts_offset(msg, offset);
+ uint8_t plen;
+
+ plen = ndp_msg_opt_pref64_plen(pref64);
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&addr, pref64->nd_opt_pref64_prefix, plen / 8);
+
+ return &addr;
+}
+
static int ndp_call_handlers(struct ndp *ndp, struct ndp_msg *msg);
static int ndp_sock_recv(struct ndp *ndp)
diff --git a/libndp/ndp_private.h b/libndp/ndp_private.h
index a75587f..e3ffb62 100644
--- a/libndp/ndp_private.h
+++ b/libndp/ndp_private.h
@@ -86,6 +86,7 @@ ndp_log_null(struct ndp *ndp, const char *format, ...) {}
#define __ND_OPT_ROUTE_INFO 24 /* rfc4191 */
#define __ND_OPT_RDNSS 25 /* rfc6106 */
#define __ND_OPT_DNSSL 31 /* rfc6106 */
+#define __ND_OPT_PREF64 38 /* rfc8781 */
struct __nd_opt_route_info { /* route information */
uint8_t nd_opt_ri_type;
@@ -112,5 +113,16 @@ struct __nd_opt_dnssl { /* DNS Search List */
char nd_opt_dnssl_domains[0];
};
+#define ND_OPT_PREF64_PLC_MASK 0x7
+#define ND_OPT_PREF64_LFT_SHIFT 3
+#define ND_OPT_PREF64_LFT_MASK (0x1FFF << ND_OPT_PREF64_LFT_SHIFT)
+
+struct __nd_opt_pref64 { /* NAT64 prefix */
+ uint8_t nd_opt_pref64_type;
+ uint8_t nd_opt_pref64_len;
+ uint16_t nd_opt_pref64_lft_plc;
+ uint8_t nd_opt_pref64_prefix[12];
+};
+
#endif /* _NDP_PRIVATE_H_ */
diff --git a/utils/ndptool.c b/utils/ndptool.c
index 618f167..0042371 100644
--- a/utils/ndptool.c
+++ b/utils/ndptool.c
@@ -300,6 +300,21 @@ static int msgrcv_handler_func(struct ndp *ndp, struct ndp_msg *msg, void *priv)
pr_out_lft(ndp_msg_opt_dnssl_lifetime(msg, offset));
pr_out("\n");
}
+ ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_PREF64) {
+ struct in6_addr *prefix;
+ uint16_t lft;
+ uint8_t plen;
+
+ pr_out(" NAT64 Prefix: ");
+
+ prefix = ndp_msg_opt_pref64_prefix(msg, offset);
+ plen = ndp_msg_opt_pref64_prefix_length(msg, offset);
+ lft = ndp_msg_opt_pref64_lifetime(msg, offset);
+
+ pr_out("%s/%u", str_in6_addr(prefix, buf), plen);
+ pr_out(", lifetime: %us", lft);
+ pr_out("\n");
+ }
} else if (msg_type == NDP_MSG_NS) {
pr_out(" Type: NS\n");
} else if (msg_type == NDP_MSG_NA) {
--
2.45.2