diff --git a/defaults.h b/defaults.h index efd6427..eca6a70 100644 --- a/defaults.h +++ b/defaults.h @@ -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 diff --git a/gram.y b/gram.y index b167f04..b759e5d 100644 --- a/gram.y +++ b/gram.y @@ -790,24 +790,15 @@ 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++; + 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)); } ; @@ -1070,6 +1061,7 @@ static void cleanup(void) } if (rdnss) { + free(rdnss->AdvRDNSSAddr); free(rdnss); rdnss = 0; } diff --git a/interface.c b/interface.c index 1417d58..d980785 100644 --- a/interface.c +++ b/interface.c @@ -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; } diff --git a/process.c b/process.c index 66fb257..c161414 100644 --- a/process.c +++ b/process.c @@ -322,44 +322,21 @@ 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); + if (rdnssinfo->nd_opt_rdnssi_len > 2) { + 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; diff --git a/radvd.h b/radvd.h index 0fa0456..6f5928d 100644 --- a/radvd.h +++ b/radvd.h @@ -198,9 +198,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; }; diff --git a/radvdump.c b/radvdump.c index b6fd600..0c4527b 100644 --- a/radvdump.c +++ b/radvdump.c @@ -402,15 +402,8 @@ static void print_ff(unsigned char *msg, int len, struct sockaddr_in6 *addr, int 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)); + 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); } diff --git a/send.c b/send.c index eb5910e..1d4393d 100644 --- a/send.c +++ b/send.c @@ -579,8 +579,10 @@ static struct safe_buffer_list *add_ra_options_route(struct safe_buffer_list *sb static struct safe_buffer_list *add_ra_options_rdnss(struct safe_buffer_list *sbl, struct Interface const *iface, struct AdvRDNSS const *rdnss, int cease_adv, struct in6_addr const *dest) { + struct safe_buffer *rdnss_addr = new_safe_buffer(); while (rdnss) { struct nd_opt_rdnss_info_local rdnssinfo; + rdnss_addr->used = 0; if (!cease_adv && !schedule_option_rdnss(dest, iface, rdnss)) { rdnss = rdnss->next; @@ -589,6 +591,12 @@ static struct safe_buffer_list *add_ra_options_rdnss(struct safe_buffer_list *sb memset(&rdnssinfo, 0, sizeof(rdnssinfo)); + size_t const bytes = sizeof(rdnssinfo) + sizeof(struct in6_addr) * rdnss->AdvRDNSSNumber; + if (bytes > (256 * 8)) { + flog(LOG_ERR, "RDNSS too long for RA option, must be < 2048 bytes. Exiting."); + exit(1); + } + 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; @@ -599,16 +607,17 @@ 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_append(sbl->sb, &rdnssinfo, sizeof(rdnssinfo)); + for (int i = 0; i < rdnss->AdvRDNSSNumber; i++) { + safe_buffer_append(rdnss_addr, &rdnss->AdvRDNSSAddr[i], sizeof(struct in6_addr)); + } + safe_buffer_append(sbl->sb, rdnss_addr->buffer, rdnss_addr->used); + safe_buffer_free(rdnss_addr); rdnss = rdnss->next; } + safe_buffer_free(rdnss_addr); return sbl; } diff --git a/util.c b/util.c index 3ff0062..c033794 100644 --- a/util.c +++ b/util.c @@ -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;