zebra: support DNS configuration options in rtadv

Add support for the RDNSS and DNSSL router advertisement
options described in RFC 8106.

Signed-off-by: Lars Seipel <ls@slrz.net>
This commit is contained in:
Lars Seipel 2019-01-26 23:51:48 +01:00
parent 41e8603bfa
commit 3eb4fbb0f5
7 changed files with 470 additions and 1 deletions

View file

@ -1738,7 +1738,8 @@ AC_CHECK_TYPES([
vifi_t, struct sioc_vif_req, struct igmpmsg,
struct ifaliasreq, struct if6_aliasreq, struct in6_aliasreq,
struct nd_opt_adv_interval, struct rt_addrinfo,
struct nd_opt_homeagent_info, struct nd_opt_adv_interval],
struct nd_opt_homeagent_info, struct nd_opt_adv_interval,
struct nd_opt_rdnss, struct nd_opt_dnssl],
[], [], FRR_INCLUDES)
AC_CHECK_MEMBERS([struct sockaddr.sa_len,

View file

@ -135,6 +135,8 @@ static int if_zebra_new_hook(struct interface *ifp)
rtadv->DefaultPreference = RTADV_PREF_MEDIUM;
rtadv->AdvPrefixList = list_new();
rtadv->AdvRDNSSList = list_new();
rtadv->AdvDNSSLList = list_new();
}
#endif /* HAVE_RTADV */
@ -175,6 +177,8 @@ static int if_zebra_delete_hook(struct interface *ifp)
rtadv = &zebra_if->rtadv;
list_delete(&rtadv->AdvPrefixList);
list_delete(&rtadv->AdvRDNSSList);
list_delete(&rtadv->AdvDNSSLList);
#endif /* HAVE_RTADV */
THREAD_OFF(zebra_if->speed_update);

View file

@ -168,6 +168,22 @@ struct rtadvconf {
int DefaultPreference;
#define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */
/*
* List of recursive DNS servers to include in the RDNSS option.
* See [RFC8106 5.1]
*
* Default: empty list; do not emit RDNSS option
*/
struct list *AdvRDNSSList;
/*
* List of DNS search domains to include in the DNSSL option.
* See [RFC8106 5.2]
*
* Default: empty list; do not emit DNSSL option
*/
struct list *AdvDNSSLList;
uint8_t inFastRexmit; /* True if we're rexmits faster than usual */
/* Track if RA was configured by BGP or by the Operator or both */
@ -182,6 +198,41 @@ struct rtadvconf {
#define RTADV_NUM_FAST_REXMITS 4 /* Fast Rexmit RA 4 times on certain events */
};
struct rtadv_rdnss {
/* Address of recursive DNS server to advertise */
struct in6_addr addr;
/*
* Lifetime in seconds; all-ones means infinity, zero
* stop using it.
*/
uint32_t lifetime;
/* If lifetime not set, use a default of 3*MaxRtrAdvInterval */
int lifetime_set;
};
/*
* [RFC1035 2.3.4] sets the maximum length of a domain name (a sequence of
* labels, each prefixed by a length octet) at 255 octets.
*/
#define RTADV_MAX_ENCODED_DOMAIN_NAME 255
struct rtadv_dnssl {
/* Domain name without trailing root zone dot (NUL-terminated) */
char name[RTADV_MAX_ENCODED_DOMAIN_NAME - 1];
/* Name encoded as in [RFC1035 3.1] */
uint8_t encoded_name[RTADV_MAX_ENCODED_DOMAIN_NAME];
/* Actual length of encoded_name */
size_t encoded_len;
/* Lifetime as for RDNSS */
uint32_t lifetime;
int lifetime_set;
};
#endif /* HAVE_RTADV */
/* Zebra interface type - ones of interest. */

View file

@ -355,6 +355,53 @@ static void rtadv_send_packet(int sock, struct interface *ifp)
len += sizeof(struct nd_opt_mtu);
}
/* Recursive DNS servers */
struct rtadv_rdnss *rdnss;
for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
struct nd_opt_rdnss *opt = (struct nd_opt_rdnss *)(buf + len);
opt->nd_opt_rdnss_type = ND_OPT_RDNSS;
opt->nd_opt_rdnss_len = 3;
opt->nd_opt_rdnss_reserved = 0;
opt->nd_opt_rdnss_lifetime = htonl(
rdnss->lifetime_set
? rdnss->lifetime
: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
len += sizeof(struct nd_opt_rdnss);
IPV6_ADDR_COPY(buf + len, &rdnss->addr);
len += sizeof(struct in6_addr);
}
/* DNS search list */
struct rtadv_dnssl *dnssl;
for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
size_t names_start;
struct nd_opt_dnssl *opt = (struct nd_opt_dnssl *)(buf + len);
opt->nd_opt_dnssl_type = ND_OPT_DNSSL;
opt->nd_opt_dnssl_len = 1;
opt->nd_opt_dnssl_reserved = 0;
opt->nd_opt_dnssl_lifetime = htonl(
dnssl->lifetime_set
? dnssl->lifetime
: MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval));
len += sizeof(struct nd_opt_dnssl);
names_start = len;
memcpy(buf + len, dnssl->encoded_name, dnssl->encoded_len);
len += dnssl->encoded_len;
/* Zero-pad to 8-octet boundary */
while (len % 8)
buf[len++] = '\0';
opt->nd_opt_dnssl_len += (len - names_start) / 8;
}
msg.msg_name = (void *)&addr;
msg.msg_namelen = sizeof(struct sockaddr_in6);
msg.msg_iov = &iov;
@ -1533,6 +1580,308 @@ DEFUN (no_ipv6_nd_mtu,
return CMD_SUCCESS;
}
static struct rtadv_rdnss *rtadv_rdnss_new(void)
{
return XCALLOC(MTYPE_RTADV_RDNSS, sizeof(struct rtadv_rdnss));
}
static void rtadv_rdnss_free(struct rtadv_rdnss *rdnss)
{
XFREE(MTYPE_RTADV_RDNSS, rdnss);
}
static struct rtadv_rdnss *rtadv_rdnss_lookup(struct list *list,
struct rtadv_rdnss *rdnss)
{
struct listnode *node;
struct rtadv_rdnss *p;
for (ALL_LIST_ELEMENTS_RO(list, node, p))
if (IPV6_ADDR_SAME(&p->addr, &rdnss->addr))
return p;
return NULL;
}
static struct rtadv_rdnss *rtadv_rdnss_get(struct list *list,
struct rtadv_rdnss *rdnss)
{
struct rtadv_rdnss *p;
p = rtadv_rdnss_lookup(list, rdnss);
if (p)
return p;
p = rtadv_rdnss_new();
memcpy(p, rdnss, sizeof(struct rtadv_rdnss));
listnode_add(list, p);
return p;
}
static void rtadv_rdnss_set(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
{
struct rtadv_rdnss *p;
p = rtadv_rdnss_get(zif->rtadv.AdvRDNSSList, rdnss);
p->lifetime = rdnss->lifetime;
p->lifetime_set = rdnss->lifetime_set;
}
static int rtadv_rdnss_reset(struct zebra_if *zif, struct rtadv_rdnss *rdnss)
{
struct rtadv_rdnss *p;
p = rtadv_rdnss_lookup(zif->rtadv.AdvRDNSSList, rdnss);
if (p) {
listnode_delete(zif->rtadv.AdvRDNSSList, p);
rtadv_rdnss_free(p);
return 1;
}
return 0;
}
static struct rtadv_dnssl *rtadv_dnssl_new(void)
{
return XCALLOC(MTYPE_RTADV_DNSSL, sizeof(struct rtadv_dnssl));
}
static void rtadv_dnssl_free(struct rtadv_dnssl *dnssl)
{
XFREE(MTYPE_RTADV_DNSSL, dnssl);
}
static struct rtadv_dnssl *rtadv_dnssl_lookup(struct list *list,
struct rtadv_dnssl *dnssl)
{
struct listnode *node;
struct rtadv_dnssl *p;
for (ALL_LIST_ELEMENTS_RO(list, node, p))
if (!strcasecmp(p->name, dnssl->name))
return p;
return NULL;
}
static struct rtadv_dnssl *rtadv_dnssl_get(struct list *list,
struct rtadv_dnssl *dnssl)
{
struct rtadv_dnssl *p;
p = rtadv_dnssl_lookup(list, dnssl);
if (p)
return p;
p = rtadv_dnssl_new();
memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
listnode_add(list, p);
return p;
}
static void rtadv_dnssl_set(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
{
struct rtadv_dnssl *p;
p = rtadv_dnssl_get(zif->rtadv.AdvDNSSLList, dnssl);
memcpy(p, dnssl, sizeof(struct rtadv_dnssl));
}
static int rtadv_dnssl_reset(struct zebra_if *zif, struct rtadv_dnssl *dnssl)
{
struct rtadv_dnssl *p;
p = rtadv_dnssl_lookup(zif->rtadv.AdvDNSSLList, dnssl);
if (p) {
listnode_delete(zif->rtadv.AdvDNSSLList, p);
rtadv_dnssl_free(p);
return 1;
}
return 0;
}
/*
* Convert dotted domain name (with or without trailing root zone dot) to
* sequence of length-prefixed labels, as described in [RFC1035 3.1]. Write up
* to strlen(in) + 2 octets to out.
*
* Returns the number of octets written to out or -1 if in does not constitute
* a valid domain name.
*/
static int rtadv_dnssl_encode(uint8_t *out, const char *in)
{
const char *label_start, *label_end;
size_t outp;
outp = 0;
label_start = in;
while (*label_start) {
size_t label_len;
label_end = strchr(label_start, '.');
if (label_end == NULL)
label_end = label_start + strlen(label_start);
label_len = label_end - label_start;
if (label_len >= 64)
return -1; /* labels must be 63 octets or less */
out[outp++] = (uint8_t)label_len;
memcpy(out + outp, label_start, label_len);
outp += label_len;
label_start += label_len;
if (*label_start == '.')
label_start++;
}
out[outp++] = '\0';
return outp;
}
DEFUN(ipv6_nd_rdnss,
ipv6_nd_rdnss_cmd,
"ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
"Interface IPv6 config commands\n"
"Neighbor discovery\n"
"Recursive DNS server information\n"
"IPv6 address\n"
"Valid lifetime in seconds\n"
"Infinite valid lifetime\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
struct rtadv_rdnss rdnss = {0};
if (inet_pton(AF_INET6, argv[3]->arg, &rdnss.addr) != 1) {
vty_out(vty, "Malformed IPv6 address\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (argc > 4) {
char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
: argv[4]->text;
rdnss.lifetime = strmatch(lifetime, "infinite")
? UINT32_MAX
: strtoll(lifetime, NULL, 10);
rdnss.lifetime_set = 1;
}
rtadv_rdnss_set(zif, &rdnss);
return CMD_SUCCESS;
}
DEFUN(no_ipv6_nd_rdnss,
no_ipv6_nd_rdnss_cmd,
"no ipv6 nd rdnss X:X::X:X [<(0-4294967295)|infinite>]",
NO_STR
"Interface IPv6 config commands\n"
"Neighbor discovery\n"
"Recursive DNS server information\n"
"IPv6 address\n"
"Valid lifetime in seconds\n"
"Infinite valid lifetime\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
struct rtadv_rdnss rdnss = {0};
if (inet_pton(AF_INET6, argv[4]->arg, &rdnss.addr) != 1) {
vty_out(vty, "Malformed IPv6 address\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (rtadv_rdnss_reset(zif, &rdnss) != 1) {
vty_out(vty, "Non-existant RDNSS address\n");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
DEFUN(ipv6_nd_dnssl,
ipv6_nd_dnssl_cmd,
"ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
"Interface IPv6 config commands\n"
"Neighbor discovery\n"
"DNS search list information\n"
"Domain name suffix\n"
"Valid lifetime in seconds\n"
"Infinite valid lifetime\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
struct rtadv_dnssl dnssl = {0};
size_t len;
int ret;
len = strlcpy(dnssl.name, argv[3]->arg, sizeof(dnssl.name));
if (len == 0 || len >= sizeof(dnssl.name)) {
vty_out(vty, "Malformed DNS search domain\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (dnssl.name[len - 1] == '.') {
/*
* Allow, but don't require, a trailing dot signifying the root
* zone. Canonicalize by cutting it off if present.
*/
dnssl.name[len - 1] = '\0';
len--;
}
if (argc > 4) {
char *lifetime = argv[4]->type == RANGE_TKN ? argv[4]->arg
: argv[4]->text;
dnssl.lifetime = strmatch(lifetime, "infinite")
? UINT32_MAX
: strtoll(lifetime, NULL, 10);
dnssl.lifetime_set = 1;
}
ret = rtadv_dnssl_encode(dnssl.encoded_name, dnssl.name);
if (ret < 0) {
vty_out(vty, "Malformed DNS search domain\n");
return CMD_WARNING_CONFIG_FAILED;
}
dnssl.encoded_len = ret;
rtadv_dnssl_set(zif, &dnssl);
return CMD_SUCCESS;
}
DEFUN(no_ipv6_nd_dnssl,
no_ipv6_nd_dnssl_cmd,
"no ipv6 nd dnssl SUFFIX [<(0-4294967295)|infinite>]",
NO_STR
"Interface IPv6 config commands\n"
"Neighbor discovery\n"
"DNS search list information\n"
"Domain name suffix\n"
"Valid lifetime in seconds\n"
"Infinite valid lifetime\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif = ifp->info;
struct rtadv_dnssl dnssl = {0};
size_t len;
len = strlcpy(dnssl.name, argv[4]->arg, sizeof(dnssl.name));
if (len == 0 || len >= sizeof(dnssl.name)) {
vty_out(vty, "Malformed DNS search domain\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (dnssl.name[len - 1] == '.') {
dnssl.name[len - 1] = '\0';
len--;
}
if (rtadv_dnssl_reset(zif, &dnssl) != 1) {
vty_out(vty, "Non-existant DNS search domain\n");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
/* Dump interface ND information to vty. */
static int nd_dump_vty(struct vty *vty, struct interface *ifp)
{
@ -1607,6 +1956,8 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
struct zebra_if *zif;
struct listnode *node;
struct rtadv_prefix *rprefix;
struct rtadv_rdnss *rdnss;
struct rtadv_dnssl *dnssl;
char buf[PREFIX_STRLEN];
int interval;
@ -1688,6 +2039,29 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp)
vty_out(vty, " router-address");
vty_out(vty, "\n");
}
for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvRDNSSList, node, rdnss)) {
char buf[INET6_ADDRSTRLEN];
vty_out(vty, " ipv6 nd rdnss %s",
inet_ntop(AF_INET6, &rdnss->addr, buf, sizeof(buf)));
if (rdnss->lifetime_set) {
if (rdnss->lifetime == UINT32_MAX)
vty_out(vty, " infinite");
else
vty_out(vty, " %u", rdnss->lifetime);
}
vty_out(vty, "\n");
}
for (ALL_LIST_ELEMENTS_RO(zif->rtadv.AdvDNSSLList, node, dnssl)) {
vty_out(vty, " ipv6 nd dnssl %s", dnssl->name);
if (dnssl->lifetime_set) {
if (dnssl->lifetime == UINT32_MAX)
vty_out(vty, " infinite");
else
vty_out(vty, " %u", dnssl->lifetime);
}
vty_out(vty, "\n");
}
return 0;
}
@ -1782,6 +2156,10 @@ void rtadv_cmd_init(void)
install_element(INTERFACE_NODE, &no_ipv6_nd_router_preference_cmd);
install_element(INTERFACE_NODE, &ipv6_nd_mtu_cmd);
install_element(INTERFACE_NODE, &no_ipv6_nd_mtu_cmd);
install_element(INTERFACE_NODE, &ipv6_nd_rdnss_cmd);
install_element(INTERFACE_NODE, &no_ipv6_nd_rdnss_cmd);
install_element(INTERFACE_NODE, &ipv6_nd_dnssl_cmd);
install_element(INTERFACE_NODE, &no_ipv6_nd_dnssl_cmd);
}
static int if_join_all_router(int sock, struct interface *ifp)

View file

@ -91,6 +91,37 @@ struct nd_opt_homeagent_info { /* Home Agent info */
} __attribute__((__packed__));
#endif
#ifndef ND_OPT_RDNSS
#define ND_OPT_RDNSS 25
#endif
#ifndef ND_OPT_DNSSL
#define ND_OPT_DNSSL 31
#endif
#ifndef HAVE_STRUCT_ND_OPT_RDNSS
struct nd_opt_rdnss { /* Recursive DNS server option [RFC8106 5.1] */
uint8_t nd_opt_rdnss_type;
uint8_t nd_opt_rdnss_len;
uint16_t nd_opt_rdnss_reserved;
uint32_t nd_opt_rdnss_lifetime;
/* Followed by one or more IPv6 addresses */
} __attribute__((__packed__));
#endif
#ifndef HAVE_STRUCT_ND_OPT_DNSSL
struct nd_opt_dnssl { /* DNS search list option [RFC8106 5.2] */
uint8_t nd_opt_dnssl_type;
uint8_t nd_opt_dnssl_len;
uint16_t nd_opt_dnssl_reserved;
uint32_t nd_opt_dnssl_lifetime;
/*
* Followed by one or more domain names encoded as in [RFC1035 3.1].
* Multiple domain names are concatenated after encoding. In any case,
* the result is zero-padded to a multiple of 8 octets.
*/
} __attribute__((__packed__));
#endif
extern const char *rtadv_pref_strs[];
#endif /* HAVE_RTADV */

View file

@ -27,6 +27,8 @@
DEFINE_MGROUP(ZEBRA, "zebra")
DEFINE_MTYPE(ZEBRA, RTADV_PREFIX, "Router Advertisement Prefix")
DEFINE_MTYPE(ZEBRA, RTADV_RDNSS, "Router Advertisement RDNSS")
DEFINE_MTYPE(ZEBRA, RTADV_DNSSL, "Router Advertisement DNSSL")
DEFINE_MTYPE(ZEBRA, ZEBRA_VRF, "ZEBRA VRF")
DEFINE_MTYPE(ZEBRA, RE, "Route Entry")
DEFINE_MTYPE(ZEBRA, RIB_QUEUE, "RIB process work queue")

View file

@ -26,6 +26,8 @@
DECLARE_MGROUP(ZEBRA)
DECLARE_MTYPE(RTADV_PREFIX)
DECLARE_MTYPE(RTADV_RDNSS)
DECLARE_MTYPE(RTADV_DNSSL)
DECLARE_MTYPE(ZEBRA_NS)
DECLARE_MTYPE(ZEBRA_VRF)
DECLARE_MTYPE(RE)