Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support more addresses in RDNSS section #245

Merged
merged 1 commit into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,7 @@ struct nd_opt_rdnss_info_local {
uint8_t nd_opt_rdnssi_len;
uint16_t nd_opt_rdnssi_pref_flag_reserved;
uint32_t nd_opt_rdnssi_lifetime;
struct in6_addr nd_opt_rdnssi_addr1;
struct in6_addr nd_opt_rdnssi_addr2;
struct in6_addr nd_opt_rdnssi_addr3;
struct in6_addr nd_opt_rdnssi_addr[];
};
/* pref/flag/reserved field : yyyyx00000000000 (big endian) - 00000000yyyyx000 (little indian); where yyyy = pref, x = flag */
#if BYTE_ORDER == BIG_ENDIAN
Expand Down
30 changes: 13 additions & 17 deletions gram.y
Original file line number Diff line number Diff line change
Expand Up @@ -847,24 +847,19 @@ rdnssaddr : IPV6ADDR
rdnss_init_defaults(rdnss, iface);
}

switch (rdnss->AdvRDNSSNumber) {
case 0:
memcpy(&rdnss->AdvRDNSSAddr1, $1, sizeof(struct in6_addr));
rdnss->AdvRDNSSNumber++;
break;
case 1:
memcpy(&rdnss->AdvRDNSSAddr2, $1, sizeof(struct in6_addr));
rdnss->AdvRDNSSNumber++;
break;
case 2:
memcpy(&rdnss->AdvRDNSSAddr3, $1, sizeof(struct in6_addr));
rdnss->AdvRDNSSNumber++;
break;
default:
flog(LOG_CRIT, "too many addresses in RDNSS section");
ABORT;
rdnss->AdvRDNSSNumber++;
if (rdnss->AdvRDNSSNumber > 127) {
flog(LOG_CRIT, "Too many RDNSS servers specified - upper limit is 127 based on RDNSSI length field being uint8, RFC8106, section 5.1");
ABORT;
}

rdnss->AdvRDNSSAddr =
realloc(rdnss->AdvRDNSSAddr,
rdnss->AdvRDNSSNumber * sizeof(struct in6_addr));
if (rdnss->AdvRDNSSAddr == NULL) {
flog(LOG_CRIT, "realloc failed: %s", strerror(errno));
ABORT;
}
memcpy(&rdnss->AdvRDNSSAddr[rdnss->AdvRDNSSNumber - 1], $1, sizeof(struct in6_addr));
}
;

Expand Down Expand Up @@ -1127,6 +1122,7 @@ static void cleanup(void)
}

if (rdnss) {
free(rdnss->AdvRDNSSAddr);
free(rdnss);
rdnss = 0;
}
Expand Down
1 change: 1 addition & 0 deletions interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ static void free_iface_list(struct Interface *iface)
while (rdnss) {
struct AdvRDNSS *next_rdnss = rdnss->next;

free(rdnss->AdvRDNSSAddr);
free(rdnss);
rdnss = next_rdnss;
}
Expand Down
47 changes: 13 additions & 34 deletions process.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,44 +322,23 @@ static void process_ra(struct Interface *iface, unsigned char *msg, int len, str
break;
case ND_OPT_RDNSS_INFORMATION: {
char rdnss_str[INET6_ADDRSTRLEN];
struct AdvRDNSS *rdnss = 0;
struct nd_opt_rdnss_info_local *rdnssinfo = (struct nd_opt_rdnss_info_local *)opt_str;
if (len < sizeof(*rdnssinfo))
return;
int count = rdnssinfo->nd_opt_rdnssi_len;

/* Check the RNDSS addresses received */
switch (count) {
case 7:
rdnss = iface->AdvRDNSSList;
if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr3)) {
/* no match found in iface->AdvRDNSSList */
addrtostr(&rdnssinfo->nd_opt_rdnssi_addr3, rdnss_str, sizeof(rdnss_str));
flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
rdnss_str, iface->props.name, addr_str);
}
/* FALLTHROUGH */
case 5:
rdnss = iface->AdvRDNSSList;
if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr2)) {
/* no match found in iface->AdvRDNSSList */
addrtostr(&rdnssinfo->nd_opt_rdnssi_addr2, rdnss_str, sizeof(rdnss_str));
flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
rdnss_str, iface->props.name, addr_str);
}
/* FALLTHROUGH */
case 3:
rdnss = iface->AdvRDNSSList;
if (!check_rdnss_presence(rdnss, &rdnssinfo->nd_opt_rdnssi_addr1)) {
/* no match found in iface->AdvRDNSSList */
addrtostr(&rdnssinfo->nd_opt_rdnssi_addr1, rdnss_str, sizeof(rdnss_str));
flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
rdnss_str, iface->props.name, addr_str);
}

break;
default:
flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s", count, iface->props.name, addr_str);
// must be a multiple of 2, in the range of 3..255
// https://datatracker.ietf.org/doc/html/rfc8106#section-5.1
if (rdnssinfo->nd_opt_rdnssi_len >= 3 && rdnssinfo->nd_opt_rdnssi_len % 2 == 1) {
for (int i = 0; i < (rdnssinfo->nd_opt_rdnssi_len - 1) / 2; i++) {
if (!check_rdnss_presence(iface->AdvRDNSSList, &rdnssinfo->nd_opt_rdnssi_addr[i])) {
addrtostr(&rdnssinfo->nd_opt_rdnssi_addr[i], rdnss_str, sizeof(rdnss_str));
flog(LOG_WARNING, "RDNSS address %s received on %s from %s is not advertised by us",
rdnss_str, iface->props.name, addr_str);
}
}
} else {
flog(LOG_ERR, "invalid len %i in RDNSS option on %s from %s",
rdnssinfo->nd_opt_rdnssi_len, iface->props.name, addr_str);
}

break;
Expand Down
4 changes: 1 addition & 3 deletions radvd.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,7 @@ struct AdvRDNSS {
int AdvRDNSSNumber;
uint32_t AdvRDNSSLifetime;
int FlushRDNSSFlag;
struct in6_addr AdvRDNSSAddr1;
struct in6_addr AdvRDNSSAddr2;
struct in6_addr AdvRDNSSAddr3;
struct in6_addr *AdvRDNSSAddr;

struct AdvRDNSS *next;
};
Expand Down
35 changes: 16 additions & 19 deletions radvdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,28 +400,25 @@ static void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int
case ND_OPT_RDNSS_INFORMATION: {
struct nd_opt_rdnss_info_local *rdnss_info = (struct nd_opt_rdnss_info_local *)opt_str;

printf("\n\tRDNSS");

addrtostr(&rdnss_info->nd_opt_rdnssi_addr1, prefix_str, sizeof(prefix_str));
printf(" %s", prefix_str);

if (rdnss_info->nd_opt_rdnssi_len >= 5) {
addrtostr(&rdnss_info->nd_opt_rdnssi_addr2, prefix_str, sizeof(prefix_str));
printf(" %s", prefix_str);
}
if (rdnss_info->nd_opt_rdnssi_len >= 7) {
addrtostr(&rdnss_info->nd_opt_rdnssi_addr3, prefix_str, sizeof(prefix_str));
printf(" %s", prefix_str);
if (rdnss_info->nd_opt_rdnssi_len >= 3 && rdnss_info->nd_opt_rdnssi_len % 2 == 1) {
printf("\n\tRDNSS");
for (int i = 0; i < (rdnss_info->nd_opt_rdnssi_len - 1) / 2; i++) {
addrtostr(&rdnss_info->nd_opt_rdnssi_addr[i], prefix_str, sizeof(prefix_str));
printf(" %s", prefix_str);
}
printf("\n\t{\n");
/* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */
if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff)
printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n");
else
printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime));
printf("\t}; # End of RDNSS definition\n\n");
} else {
flog(LOG_ERR, "Invalid RDNSS option length %d from %s", rdnss_info->nd_opt_rdnssi_len, addr_str);
printf("# Invalid RDNSS length %d, per RFC8106 section 5.1 ", rdnss_info->nd_opt_rdnssi_len);
}

printf("\n\t{\n");
/* as AdvRDNSSLifetime may depend on MaxRtrAdvInterval, it could change */
if (ntohl(rdnss_info->nd_opt_rdnssi_lifetime) == 0xffffffff)
printf("\t\tAdvRDNSSLifetime infinity; # (0xffffffff)\n");
else
printf("\t\tAdvRDNSSLifetime %u;\n", ntohl(rdnss_info->nd_opt_rdnssi_lifetime));

printf("\t}; # End of RDNSS definition\n\n");
break;
}
case ND_OPT_DNSSL_INFORMATION: {
Expand Down
23 changes: 16 additions & 7 deletions send.c
Original file line number Diff line number Diff line change
Expand Up @@ -602,14 +602,23 @@ static struct safe_buffer_list *add_ra_options_rdnss(struct safe_buffer_list *sb
{
while (rdnss) {
struct nd_opt_rdnss_info_local rdnssinfo;

if (!cease_adv && !schedule_option_rdnss(dest, iface, rdnss)) {
rdnss = rdnss->next;
continue;
}

memset(&rdnssinfo, 0, sizeof(rdnssinfo));

size_t const bytes = sizeof(rdnssinfo) + sizeof(struct in6_addr) * rdnss->AdvRDNSSNumber;
// dnsslinfo.nd_opt_rdnssi_len is uint8 count of 8-octet groups; min 3, max 255
// too many DNS servers could exceed it
// https://datatracker.ietf.org/doc/html/rfc8106#section-5.1
if (bytes > (255 * 8)) {
flog(LOG_ERR, "RDNSS too long for RA option, must be < 2032 bytes. Skipping option.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the other PR - shouldn't this be <= 2040 bytes? Or am I not understanding the math?

rdnss = rdnss->next;
continue;
}

rdnssinfo.nd_opt_rdnssi_type = ND_OPT_RDNSS_INFORMATION;
rdnssinfo.nd_opt_rdnssi_len = 1 + 2 * rdnss->AdvRDNSSNumber;
rdnssinfo.nd_opt_rdnssi_pref_flag_reserved = 0;
Expand All @@ -620,13 +629,13 @@ static struct safe_buffer_list *add_ra_options_rdnss(struct safe_buffer_list *sb
rdnssinfo.nd_opt_rdnssi_lifetime = htonl(rdnss->AdvRDNSSLifetime);
}

memcpy(&rdnssinfo.nd_opt_rdnssi_addr1, &rdnss->AdvRDNSSAddr1, sizeof(struct in6_addr));
memcpy(&rdnssinfo.nd_opt_rdnssi_addr2, &rdnss->AdvRDNSSAddr2, sizeof(struct in6_addr));
memcpy(&rdnssinfo.nd_opt_rdnssi_addr3, &rdnss->AdvRDNSSAddr3, sizeof(struct in6_addr));

sbl = safe_buffer_list_append(sbl);
safe_buffer_append(sbl->sb, &rdnssinfo,
sizeof(rdnssinfo) - (3 - rdnss->AdvRDNSSNumber) * sizeof(struct in6_addr));
safe_buffer_resize(sbl->sb, sbl->sb->used + bytes);
safe_buffer_append(sbl->sb, &rdnssinfo, sizeof(rdnssinfo));
for (int i = 0; i < rdnss->AdvRDNSSNumber; i++) {
safe_buffer_append(sbl->sb, &rdnss->AdvRDNSSAddr[i], sizeof(struct in6_addr));
}
// padding is only required for DNSSL

rdnss = rdnss->next;
}
Expand Down
8 changes: 4 additions & 4 deletions util.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ void addrtostr(struct in6_addr const *addr, char *str, size_t str_size)
int check_rdnss_presence(struct AdvRDNSS *rdnss, struct in6_addr *addr)
{
while (rdnss) {
if (!memcmp(&rdnss->AdvRDNSSAddr1, addr, sizeof(struct in6_addr)) ||
!memcmp(&rdnss->AdvRDNSSAddr2, addr, sizeof(struct in6_addr)) ||
!memcmp(&rdnss->AdvRDNSSAddr3, addr, sizeof(struct in6_addr)))
return 1; /* rdnss address found in the list */
for (int i = 0; i < rdnss->AdvRDNSSNumber; i++) {
if (!memcmp(&rdnss->AdvRDNSSAddr[i], addr, sizeof(struct in6_addr)))
return 1; /* rdnss address found in the list */
}
rdnss = rdnss->next;
}
return 0;
Expand Down
Loading