summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-08-27 13:34:17 +0300
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-10-06 02:18:41 +0300
commit2600050acd74bf6cea320e8d5f3c7ae09568b062 (patch)
tree7a9c88fe7523e5a69137545eba555cc02d38c22d
parentd33028de8b8b520dfde12d9d9d01f4cd576f23b3 (diff)
downloadaccel-ppp-2600050acd74bf6cea320e8d5f3c7ae09568b062.tar.gz
accel-ppp-2600050acd74bf6cea320e8d5f3c7ae09568b062.zip
ipv6_nd: do not overrun the advertisement buffer with prefix information
The prefix information options are written into a fixed 1024 byte pool buffer with no bound at all, one 32 byte option per address on the session. A session carrying more than about thirty addresses therefore writes past the end of the buffer. Skip the prefix information of the addresses which no longer fit and warn about them. The loop also installs the addresses on the interface, so keep doing that regardless of how much room is left in the advertisement.
-rw-r--r--accel-pppd/ipv6/nd.c37
1 files changed, 25 insertions, 12 deletions
diff --git a/accel-pppd/ipv6/nd.c b/accel-pppd/ipv6/nd.c
index 8b22da14..815b822a 100644
--- a/accel-pppd/ipv6/nd.c
+++ b/accel-pppd/ipv6/nd.c
@@ -109,6 +109,8 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ds
struct ipv6db_addr_t *a;
struct in6_addr addr, peer_addr;
struct in6_addr dns[MAX_DNS_COUNT];
+ char str[INET6_ADDRSTRLEN];
+ void *bufend;
int i, prefix_len, dns_count;
if (!buf) {
@@ -132,20 +134,31 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ds
adv->nd_ra_retransmit = htonl(conf_AdvRetransTimer);
pinfo = (struct nd_opt_prefix_info *)(adv + 1);
+ bufend = (uint8_t *)buf + BUF_SIZE;
list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
prefix_len = a->prefix_len == 128 ? 64 : a->prefix_len;
- memset(pinfo, 0, sizeof(*pinfo));
- pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
- pinfo->nd_opt_pi_len = 4;
- pinfo->nd_opt_pi_prefix_len = prefix_len;
- pinfo->nd_opt_pi_flags_reserved =
- ((a->flag_onlink || conf_AdvPrefixOnLinkFlag) ? ND_OPT_PI_FLAG_ONLINK : 0) |
- ((a->flag_auto || (conf_AdvPrefixAutonomousFlag && prefix_len == 64)) ? ND_OPT_PI_FLAG_AUTO : 0);
- pinfo->nd_opt_pi_valid_time = htonl(conf_AdvPrefixValidLifetime);
- pinfo->nd_opt_pi_preferred_time = htonl(conf_AdvPrefixPreferredLifetime);
- memcpy(&pinfo->nd_opt_pi_prefix, &a->addr, (prefix_len + 7) / 8);
- pinfo->nd_opt_pi_prefix.s6_addr[prefix_len / 8] &= ~(0xff >> (prefix_len % 8));
- pinfo++;
+
+ /* Addresses are installed even when the advertisement is
+ already full, only their prefix information is dropped */
+ if ((void *)(pinfo + 1) > bufend) {
+ log_ppp_warn("ipv6_nd: prefix %s/%i does not fit into the"
+ " router advertisement, not advertising it\n",
+ inet_ntop(AF_INET6, &a->addr, str, sizeof(str)),
+ prefix_len);
+ } else {
+ memset(pinfo, 0, sizeof(*pinfo));
+ pinfo->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ pinfo->nd_opt_pi_len = 4;
+ pinfo->nd_opt_pi_prefix_len = prefix_len;
+ pinfo->nd_opt_pi_flags_reserved =
+ ((a->flag_onlink || conf_AdvPrefixOnLinkFlag) ? ND_OPT_PI_FLAG_ONLINK : 0) |
+ ((a->flag_auto || (conf_AdvPrefixAutonomousFlag && prefix_len == 64)) ? ND_OPT_PI_FLAG_AUTO : 0);
+ pinfo->nd_opt_pi_valid_time = htonl(conf_AdvPrefixValidLifetime);
+ pinfo->nd_opt_pi_preferred_time = htonl(conf_AdvPrefixPreferredLifetime);
+ memcpy(&pinfo->nd_opt_pi_prefix, &a->addr, (prefix_len + 7) / 8);
+ pinfo->nd_opt_pi_prefix.s6_addr[prefix_len / 8] &= ~(0xff >> (prefix_len % 8));
+ pinfo++;
+ }
if (!a->installed) {
if (a->prefix_len == 128) {