From: "Theo Gaige (Schneider Electric)" <[email protected]>
Backport patch [1] mentionned in [2] and commit [3] which is dependency of [1] [1] https://github.com/NetworkConfiguration/dhcpcd/commit/708b4a56bae080a5b18c2e0c4c6fbe103131a2b0 [2] https://security-tracker.debian.org/tracker/CVE-2026-56116 [3] https://github.com/NetworkConfiguration/dhcpcd/commit/f1cf924ad691bc1e6bf33013407fbf838fa40fbe Signed-off-by: Theo Gaige (Schneider Electric) <[email protected]> --- .../dhcpcd/dhcpcd_10.0.6.bb | 2 + .../dhcpcd/files/CVE-2026-56116-pre.patch | 482 ++++++++++++++++++ .../dhcpcd/files/CVE-2026-56116.patch | 31 ++ 3 files changed, 515 insertions(+) create mode 100644 meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116-pre.patch create mode 100644 meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116.patch diff --git a/meta/recipes-connectivity/dhcpcd/dhcpcd_10.0.6.bb b/meta/recipes-connectivity/dhcpcd/dhcpcd_10.0.6.bb index bc87b91503..4a031cefea 100644 --- a/meta/recipes-connectivity/dhcpcd/dhcpcd_10.0.6.bb +++ b/meta/recipes-connectivity/dhcpcd/dhcpcd_10.0.6.bb @@ -17,6 +17,8 @@ SRC_URI = "git://github.com/NetworkConfiguration/dhcpcd;protocol=https;branch=ma file://0001-dhcpcd.8-Fix-conflict-error-when-enable-multilib.patch \ file://CVE-2026-56113.patch \ file://CVE-2026-56114.patch \ + file://CVE-2026-56116-pre.patch \ + file://CVE-2026-56116.patch \ " SRCREV = "1c8ae59836fa87b4c63c598087f0460ec20ed862" diff --git a/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116-pre.patch b/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116-pre.patch new file mode 100644 index 0000000000..f9309de80a --- /dev/null +++ b/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116-pre.patch @@ -0,0 +1,482 @@ +From b13d64e33c4b02c4806857c49e4ccaa64b6015de Mon Sep 17 00:00:00 2001 +From: Daniel Goertzen <[email protected]> +Date: Fri, 8 Mar 2024 19:27:57 -0600 +Subject: [PATCH 1/2] add RFC4191 support (#297) + +* add RFC4191 support + +- handles route information options from RAs. +- refactor `sa_fromprefix()` to expose lower level functionality +- refactor `ipv6nd_rtprefix()` to be usable outside of `struct ra` context + +* changes as requested by RM + +- mostly minor/cosmetic changes +- functional change: "no longer a default router" warning moved to capture changes from routeinfo options + +* simplify routeinfo_find/new + +(cherry picked from commit f1cf924ad691bc1e6bf33013407fbf838fa40fbe) + +This commit is a dependency of commit 708b4a56bae080a5b18c2e0c4c6fbe103131a2b0. + +Upstream-Status: Backport [https://github.com/NetworkConfiguration/dhcpcd/commit/f1cf924ad691bc1e6bf33013407fbf838fa40fbe] +Signed-off-by: Theo Gaige (Schneider Electric) <[email protected]> +--- + src/ipv6.c | 29 ++++++++++- + src/ipv6nd.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++---- + src/ipv6nd.h | 19 +++++++- + src/sa.c | 46 ++++++++++++------ + src/sa.h | 1 + + 5 files changed, 201 insertions(+), 29 deletions(-) + +diff --git a/src/ipv6.c b/src/ipv6.c +index eb8c617a..ce985d4e 100644 +--- a/src/ipv6.c ++++ b/src/ipv6.c +@@ -2318,7 +2318,9 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) + { + struct rt *rt; + struct ra *rap; ++ const struct routeinfo *rinfo; + const struct ipv6_addr *addr; ++ struct in6_addr netmask; + + if (ctx->ra_routers == NULL) + return 0; +@@ -2326,6 +2328,27 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) + TAILQ_FOREACH(rap, ctx->ra_routers, next) { + if (rap->expired) + continue; ++ ++ /* add rfc4191 route information routes */ ++ TAILQ_FOREACH (rinfo, &rap->rinfos, next) { ++ if(rinfo->lifetime == 0) ++ continue; ++ if ((rt = inet6_makeroute(rap->iface, rap)) == NULL) ++ continue; ++ ++ in6_addr_fromprefix(&netmask, rinfo->prefix_len); ++ ++ sa_in6_init(&rt->rt_dest, &rinfo->prefix); ++ sa_in6_init(&rt->rt_netmask, &netmask); ++ sa_in6_init(&rt->rt_gateway, &rap->from); ++#ifdef HAVE_ROUTE_PREF ++ rt->rt_pref = ipv6nd_rtpref(rinfo->flags); ++#endif ++ ++ rt_proto_add(routes, rt); ++ } ++ ++ /* add subnet routes */ + TAILQ_FOREACH(addr, &rap->addrs, next) { + if (addr->prefix_vltime == 0) + continue; +@@ -2333,11 +2356,13 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) + if (rt) { + rt->rt_dflags |= RTDF_RA; + #ifdef HAVE_ROUTE_PREF +- rt->rt_pref = ipv6nd_rtpref(rap); ++ rt->rt_pref = ipv6nd_rtpref(rap->flags); + #endif + rt_proto_add(routes, rt); + } + } ++ ++ /* add default route */ + if (rap->lifetime == 0) + continue; + if (ipv6_anyglobal(rap->iface) == NULL) +@@ -2347,7 +2372,7 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) + continue; + rt->rt_dflags |= RTDF_RA; + #ifdef HAVE_ROUTE_PREF +- rt->rt_pref = ipv6nd_rtpref(rap); ++ rt->rt_pref = ipv6nd_rtpref(rap->flags); + #endif + rt_proto_add(routes, rt); + } +diff --git a/src/ipv6nd.c b/src/ipv6nd.c +index 9bf7c5df..9264dce7 100644 +--- a/src/ipv6nd.c ++++ b/src/ipv6nd.c +@@ -71,6 +71,20 @@ + #define ND_OPT_PI_FLAG_ROUTER 0x20 /* Router flag in PI */ + #endif + ++#ifndef ND_OPT_RI ++#define ND_OPT_RI 24 ++struct nd_opt_ri { /* Route Information option RFC4191 */ ++ uint8_t nd_opt_ri_type; ++ uint8_t nd_opt_ri_len; ++ uint8_t nd_opt_ri_prefixlen; ++ uint8_t nd_opt_ri_flags_reserved; ++ uint32_t nd_opt_ri_lifetime; ++ struct in6_addr nd_opt_ri_prefix; ++}; ++__CTASSERT(sizeof(struct nd_opt_ri) == 24); ++#define OPT_RI_FLAG_PREFERENCE(flags) ((flags & 0x18) >> 3) ++#endif ++ + #ifndef ND_OPT_RDNSS + #define ND_OPT_RDNSS 25 + struct nd_opt_rdnss { /* RDNSS option RFC 6106 */ +@@ -132,6 +146,8 @@ __CTASSERT(sizeof(struct nd_opt_dnssl) == 8); + // + + static void ipv6nd_handledata(void *, unsigned short); ++static struct routeinfo *routeinfo_findalloc(struct ra *, const struct in6_addr *, uint8_t); ++static void routeinfohead_free(struct routeinfohead *); + + /* + * Android ships buggy ICMP6 filter headers. +@@ -612,10 +628,10 @@ ipv6nd_startexpire(struct interface *ifp) + } + + int +-ipv6nd_rtpref(struct ra *rap) ++ipv6nd_rtpref(uint8_t flags) + { + +- switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) { ++ switch (flags & ND_RA_FLAG_RTPREF_MASK) { + case ND_RA_FLAG_RTPREF_HIGH: + return RTPREF_HIGH; + case ND_RA_FLAG_RTPREF_MEDIUM: +@@ -624,7 +640,7 @@ ipv6nd_rtpref(struct ra *rap) + case ND_RA_FLAG_RTPREF_LOW: + return RTPREF_LOW; + default: +- logerrx("%s: impossible RA flag %x", __func__, rap->flags); ++ logerrx("%s: impossible RA flag %x", __func__, flags); + return RTPREF_INVALID; + } + /* NOTREACHED */ +@@ -649,7 +665,7 @@ ipv6nd_sortrouters(struct dhcpcd_ctx *ctx) + continue; + if (!ra1->isreachable && ra2->reachable) + continue; +- if (ipv6nd_rtpref(ra1) <= ipv6nd_rtpref(ra2)) ++ if (ipv6nd_rtpref(ra1->flags) <= ipv6nd_rtpref(ra2->flags)) + continue; + /* All things being equal, prefer older routers. */ + /* We don't need to check time, becase newer +@@ -827,6 +843,7 @@ ipv6nd_removefreedrop_ra(struct ra *rap, int remove_ra, int drop_ra) + if (remove_ra) + TAILQ_REMOVE(rap->iface->ctx->ra_routers, rap, next); + ipv6_freedrop_addrs(&rap->addrs, drop_ra, NULL); ++ routeinfohead_free(&rap->rinfos); + free(rap->data); + free(rap); + } +@@ -1105,6 +1122,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, + struct nd_opt_prefix_info pi; + struct nd_opt_mtu mtu; + struct nd_opt_rdnss rdnss; ++ struct nd_opt_ri ri; ++ struct routeinfo *rinfo; + uint8_t *p; + struct ra *rap; + struct in6_addr pi_prefix; +@@ -1206,6 +1225,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, + rap->from = from->sin6_addr; + strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom)); + TAILQ_INIT(&rap->addrs); ++ TAILQ_INIT(&rap->rinfos); + new_rap = true; + rap->isreachable = true; + } else +@@ -1237,9 +1257,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, + rap->flags = nd_ra->nd_ra_flags_reserved; + old_lifetime = rap->lifetime; + rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); +- if (!new_rap && rap->lifetime == 0 && old_lifetime != 0) +- logwarnx("%s: %s: no longer a default router (lifetime = 0)", +- ifp->name, rap->sfrom); + if (nd_ra->nd_ra_curhoplimit != 0) + rap->hoplimit = nd_ra->nd_ra_curhoplimit; + else +@@ -1502,6 +1519,46 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, + rdnss.nd_opt_rdnss_len > 1) + rap->hasdns = 1; + break; ++ case ND_OPT_RI: ++ if (ndo.nd_opt_len > 3) { ++ logmessage(loglevel, "%s: invalid route info option", ++ ifp->name); ++ break; ++ } ++ memset(&ri, 0, sizeof(ri)); ++ memcpy(&ri, p, olen); /* may be smaller than sizeof(ri), pad with zero */ ++ if(ri.nd_opt_ri_prefixlen > 128) { ++ logmessage(loglevel, "%s: invalid route info prefix length", ++ ifp->name); ++ break; ++ } ++ ++ /* rfc4191 3.1 - RI for ::/0 applies to default route */ ++ if(ri.nd_opt_ri_prefixlen == 0) { ++ rap->lifetime = ntohl(ri.nd_opt_ri_lifetime); ++ ++ /* Update preference leaving other flags intact */ ++ rap->flags = ((rap->flags & (~ (unsigned int)ND_RA_FLAG_RTPREF_MASK)) ++ | ri.nd_opt_ri_flags_reserved) & 0xff; ++ ++ break; ++ } ++ ++ /* Update existing route info instead of rebuilding all routes so that ++ previously announced but now absent routes can stay alive. To kill a ++ route early, an RI with lifetime=0 needs to be received (rfc4191 3.1)*/ ++ rinfo = routeinfo_findalloc(rap, &ri.nd_opt_ri_prefix, ri.nd_opt_ri_prefixlen); ++ if(rinfo == NULL) { ++ logerr(__func__); ++ break; ++ } ++ ++ /* Update/initialize other route info params */ ++ rinfo->flags = ri.nd_opt_ri_flags_reserved; ++ rinfo->lifetime = ntohl(ri.nd_opt_ri_lifetime); ++ rinfo->acquired = rap->acquired; ++ ++ break; + default: + continue; + } +@@ -1537,6 +1594,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, + ia->prefix_pltime = 0; + } + ++ if (!new_rap && rap->lifetime == 0 && old_lifetime != 0) ++ logwarnx("%s: %s: no longer a default router (lifetime = 0)", ++ ifp->name, rap->sfrom); ++ + if (new_data && !has_address && rap->lifetime && !ipv6_anyglobal(ifp)) + logwarnx("%s: no global addresses for default route", + ifp->name); +@@ -1699,7 +1760,7 @@ ipv6nd_env(FILE *fp, const struct interface *ifp) + return -1; + if (efprintf(fp, "%s_hoplimit=%u", ndprefix, rap->hoplimit) == -1) + return -1; +- pref = ipv6nd_rtpref(rap); ++ pref = ipv6nd_rtpref(rap->flags); + if (efprintf(fp, "%s_flags=%s%s%s%s%s", ndprefix, + rap->flags & ND_RA_FLAG_MANAGED ? "M" : "", + rap->flags & ND_RA_FLAG_OTHER ? "O" : "", +@@ -1804,6 +1865,7 @@ ipv6nd_expirera(void *arg) + uint32_t elapsed; + bool expired, valid; + struct ipv6_addr *ia; ++ struct routeinfo *rinfo, *rinfob; + size_t len, olen; + uint8_t *p; + struct nd_opt_hdr ndo; +@@ -1823,7 +1885,8 @@ ipv6nd_expirera(void *arg) + if (rap->iface != ifp || rap->expired) + continue; + valid = false; +- if (rap->lifetime) { ++ /* lifetime may be set to infinite by rfc4191 route information */ ++ if (rap->lifetime && rap->lifetime != ND6_INFINITE_LIFETIME) { + elapsed = (uint32_t)eloop_timespec_diff(&now, + &rap->acquired, NULL); + if (elapsed >= rap->lifetime || rap->doexpire) { +@@ -1879,6 +1942,20 @@ ipv6nd_expirera(void *arg) + } + } + ++ /* Expire route information */ ++ TAILQ_FOREACH_SAFE(rinfo, &rap->rinfos, next, rinfob) { ++ if (rinfo->lifetime == ND6_INFINITE_LIFETIME && ++ !rap->doexpire) ++ continue; ++ elapsed = (uint32_t)eloop_timespec_diff(&now, ++ &rinfo->acquired, NULL); ++ if (elapsed >= rinfo->lifetime || rap->doexpire) { ++ logwarnx("%s: expired route %s", ++ rap->iface->name, rinfo->sprefix); ++ TAILQ_REMOVE(&rap->rinfos, rinfo, next); ++ } ++ } ++ + /* Work out expiry for ND options */ + elapsed = (uint32_t)eloop_timespec_diff(&now, + &rap->acquired, NULL); +@@ -2135,3 +2212,43 @@ ipv6nd_startrs(struct interface *ifp) + eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs1, ifp); + return; + } ++ ++static struct routeinfo *routeinfo_findalloc(struct ra *rap, const struct in6_addr *prefix, uint8_t prefix_len) ++{ ++ struct routeinfo *ri; ++ char buf[INET6_ADDRSTRLEN]; ++ const char *p; ++ ++ TAILQ_FOREACH(ri, &rap->rinfos, next) { ++ if (ri->prefix_len == prefix_len && ++ IN6_ARE_ADDR_EQUAL(&ri->prefix, prefix)) ++ return ri; ++ } ++ ++ ri = malloc(sizeof(struct routeinfo)); ++ if (ri == NULL) ++ return NULL; ++ ++ memcpy(&ri->prefix, prefix, sizeof(ri->prefix)); ++ ri->prefix_len = prefix_len; ++ p = inet_ntop(AF_INET6, prefix, buf, sizeof(buf)); ++ if (p) ++ snprintf(ri->sprefix, ++ sizeof(ri->sprefix), ++ "%s/%d", ++ p, prefix_len); ++ else ++ ri->sprefix[0] = '\0'; ++ TAILQ_INSERT_TAIL(&rap->rinfos, ri, next); ++ return ri; ++} ++ ++static void routeinfohead_free(struct routeinfohead *head) ++{ ++ struct routeinfo *ri; ++ ++ while ((ri = TAILQ_FIRST(head))) { ++ TAILQ_REMOVE(head, ri, next); ++ free(ri); ++ } ++} +diff --git a/src/ipv6nd.h b/src/ipv6nd.h +index b702c3bd..837b7d0f 100644 +--- a/src/ipv6nd.h ++++ b/src/ipv6nd.h +@@ -37,6 +37,20 @@ + #include "dhcpcd.h" + #include "ipv6.h" + ++/* rfc4191 */ ++struct routeinfo { ++ TAILQ_ENTRY(routeinfo) next; ++ struct in6_addr prefix; ++ uint8_t prefix_len; ++ uint32_t lifetime; ++ uint8_t flags; ++ struct timespec acquired; ++ char sprefix[INET6_ADDRSTRLEN]; ++}; ++ ++TAILQ_HEAD(routeinfohead, routeinfo); ++ ++ + struct ra { + TAILQ_ENTRY(ra) next; + struct interface *iface; +@@ -45,13 +59,14 @@ struct ra { + uint8_t *data; + size_t data_len; + struct timespec acquired; +- unsigned char flags; ++ uint8_t flags; + uint32_t lifetime; + uint32_t reachable; + uint32_t retrans; + uint32_t mtu; + uint8_t hoplimit; + struct ipv6_addrhead addrs; ++ struct routeinfohead rinfos; + bool hasdns; + bool expired; + bool willexpire; +@@ -105,7 +120,7 @@ int ipv6nd_open(bool); + int ipv6nd_openif(struct interface *); + #endif + void ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *); +-int ipv6nd_rtpref(struct ra *); ++int ipv6nd_rtpref(uint8_t); + void ipv6nd_printoptions(const struct dhcpcd_ctx *, + const struct dhcp_opt *, size_t); + void ipv6nd_startrs(struct interface *); +diff --git a/src/sa.c b/src/sa.c +index f1e2e16e..05009d3b 100644 +--- a/src/sa.c ++++ b/src/sa.c +@@ -300,11 +300,39 @@ sa_toprefix(const struct sockaddr *sa) + return prefix; + } + ++static void ++ipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix) ++{ ++ int bytes, bits, i; ++ ++ bytes = prefix / NBBY; ++ bits = prefix % NBBY; ++ ++ for (i = 0; i < bytes; i++) ++ *ap++ = 0xff; ++ if (bits) { ++ uint8_t a; ++ ++ a = 0xff; ++ a = (uint8_t)(a << (8 - bits)); ++ *ap++ = a; ++ } ++ bytes = (max_prefix - prefix) / NBBY; ++ for (i = 0; i < bytes; i++) ++ *ap++ = 0x00; ++} ++ ++void ++in6_addr_fromprefix(struct in6_addr *addr, int prefix) ++{ ++ ipbytes_fromprefix((uint8_t *)addr, prefix, 128); ++} ++ + int + sa_fromprefix(struct sockaddr *sa, int prefix) + { + uint8_t *ap; +- int max_prefix, bytes, bits, i; ++ int max_prefix; + + switch (sa->sa_family) { + #ifdef INET +@@ -328,22 +356,8 @@ sa_fromprefix(struct sockaddr *sa, int prefix) + return -1; + } + +- bytes = prefix / NBBY; +- bits = prefix % NBBY; +- + ap = (uint8_t *)sa + sa_addroffset(sa); +- for (i = 0; i < bytes; i++) +- *ap++ = 0xff; +- if (bits) { +- uint8_t a; +- +- a = 0xff; +- a = (uint8_t)(a << (8 - bits)); +- *ap++ = a; +- } +- bytes = (max_prefix - prefix) / NBBY; +- for (i = 0; i < bytes; i++) +- *ap++ = 0x00; ++ ipbytes_fromprefix(ap, prefix, max_prefix); + + #ifndef NDEBUG + /* Ensure the calculation is correct */ +diff --git a/src/sa.h b/src/sa.h +index a848defd..902229af 100644 +--- a/src/sa.h ++++ b/src/sa.h +@@ -67,6 +67,7 @@ bool sa_is_loopback(const struct sockaddr *); + void *sa_toaddr(struct sockaddr *); + int sa_toprefix(const struct sockaddr *); + int sa_fromprefix(struct sockaddr *, int); ++void in6_addr_fromprefix(struct in6_addr *, int); + const char *sa_addrtop(const struct sockaddr *, char *, socklen_t); + int sa_cmp(const struct sockaddr *, const struct sockaddr *); + void sa_in_init(struct sockaddr *, const struct in_addr *); +-- +2.43.0 + diff --git a/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116.patch b/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116.patch new file mode 100644 index 0000000000..52d4eb5aa7 --- /dev/null +++ b/meta/recipes-connectivity/dhcpcd/files/CVE-2026-56116.patch @@ -0,0 +1,31 @@ +From 6d1a9c5118dc7910667888ef69c3f79379f427ee Mon Sep 17 00:00:00 2001 +From: Roy Marples <[email protected]> +Date: Tue, 23 Jun 2026 00:34:58 +0100 +Subject: [PATCH 2/2] IPv6ND: Free routeinfo when it expires (#670) + +Reported-by: CuB3y0nd <[email protected]> + +(cherry picked from commit 708b4a56bae080a5b18c2e0c4c6fbe103131a2b0) + +CVE: CVE-2026-56116 +Upstream-Status: Backport [https://github.com/NetworkConfiguration/dhcpcd/commit/708b4a56bae080a5b18c2e0c4c6fbe103131a2b0] +Signed-off-by: Theo Gaige (Schneider Electric) <[email protected]> +--- + src/ipv6nd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/ipv6nd.c b/src/ipv6nd.c +index 9264dce7..75655ab1 100644 +--- a/src/ipv6nd.c ++++ b/src/ipv6nd.c +@@ -1953,6 +1953,7 @@ ipv6nd_expirera(void *arg) + logwarnx("%s: expired route %s", + rap->iface->name, rinfo->sprefix); + TAILQ_REMOVE(&rap->rinfos, rinfo, next); ++ free(rinfo); + } + } + +-- +2.43.0 + -- 2.43.0
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#239973): https://lists.openembedded.org/g/openembedded-core/message/239973 Mute This Topic: https://lists.openembedded.org/mt/120061978/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
