diff --git a/sys/netproto/802_11/wlan/ieee80211_hwmp.c b/sys/netproto/802_11/wlan/ieee80211_hwmp.c --- a/sys/netproto/802_11/wlan/ieee80211_hwmp.c +++ b/sys/netproto/802_11/wlan/ieee80211_hwmp.c @@ -134,6 +134,26 @@ #define HWMP_SEQ_MAX(a, b) (a > b ? a : b) /* + * Saturating add for HWMP path/airtime metrics. + * + * HWMP frame metrics (preq_metric/prep_metric/rann_metric) are read verbatim + * from attacker-controlled on-air frames via le32dec() with no range check, so + * a crafted value near UINT32_MAX can make `accumulated += link_metric` wrap + * modulo 2^32 to a small value. That wrapped value then beats every honest + * path metric in the route-accept comparison and silently poisons / hijacks + * the route table. The 802.11s HWMP spec mandates a monotonically increasing, + * non-wrapping metric accumulation; clamp the sum at UINT32_MAX to honour that + * and defeat the wrap. See DF-0351. + */ +static __inline uint32_t +hwmp_metric_add(uint32_t a, uint32_t b) +{ + uint64_t s = (uint64_t)a + (uint64_t)b; + + return (s > UINT32_MAX) ? UINT32_MAX : (uint32_t)s; +} + +/* * Private extension of ieee80211_mesh_route. */ struct ieee80211_hwmp_route { @@ -1086,7 +1106,8 @@ /* Data creation and update of forwarding information * according to Table 11C-8 for originator mesh STA. */ - metric = preq->preq_metric + ms->ms_pmetric->mpm_metric(ni); + metric = hwmp_metric_add(preq->preq_metric, + ms->ms_pmetric->mpm_metric(ni)); if (HWMP_SEQ_GT(preq->preq_origseq, hrorig->hr_seq) || (HWMP_SEQ_EQ(preq->preq_origseq, hrorig->hr_seq) && metric < rtorig->rt_metric)) { @@ -1322,7 +1343,8 @@ #endif ppreq.preq_hopcount += 1; ppreq.preq_ttl -= 1; - ppreq.preq_metric += ms->ms_pmetric->mpm_metric(ni); + ppreq.preq_metric = hwmp_metric_add(ppreq.preq_metric, + ms->ms_pmetric->mpm_metric(ni)); /* don't do PREQ ratecheck when we propagate */ hwmp_send_preq(vap, broadcastaddr, &ppreq, NULL, NULL); @@ -1450,7 +1472,8 @@ } hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); /* update path metric */ - metric = prep->prep_metric + ms->ms_pmetric->mpm_metric(ni); + metric = hwmp_metric_add(prep->prep_metric, + ms->ms_pmetric->mpm_metric(ni)); if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { if (HWMP_SEQ_LT(prep->prep_targetseq, hr->hr_seq)) { #if defined(__DragonFly__) @@ -1554,7 +1577,8 @@ memcpy(&pprep, prep, sizeof(pprep)); pprep.prep_hopcount += 1; pprep.prep_ttl -= 1; - pprep.prep_metric += ms->ms_pmetric->mpm_metric(ni); + pprep.prep_metric = hwmp_metric_add(pprep.prep_metric, + ms->ms_pmetric->mpm_metric(ni)); hwmp_send_prep(vap, rtorig->rt_nexthop, &pprep); /* precursor list for the Target Mesh STA Address is updated */ @@ -1969,7 +1993,8 @@ /* RANN ACCEPTED */ ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */ - metric = rann->rann_metric + ms->ms_pmetric->mpm_metric(ni); + metric = hwmp_metric_add(rann->rann_metric, + ms->ms_pmetric->mpm_metric(ni)); if (rt == NULL) { rt = ieee80211_mesh_rt_add(vap, rann->rann_addr);