There are no issues with the PERR processing itself; however, to maintain consistency with the previous PREQ/PREP code modifications, I will create a new mesh_path_parse_error_frame() function to separately implement the frame format validation and the "not supported" check. Signed-off-by: Masashi Honma --- include/linux/ieee80211-mesh.h | 41 ++++++++++++++++++++++++++++++++++ net/mac80211/mesh_hwmp.c | 14 ++++++++++-- net/mac80211/parse.c | 9 ++++++-- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h index b4fca2937de0..bd83c647a578 100644 --- a/include/linux/ieee80211-mesh.h +++ b/include/linux/ieee80211-mesh.h @@ -365,4 +365,45 @@ static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen) return elen == needed; } +/* IEEE Std 802.11-2016 9.4.2.115 PERR element */ +static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen) +{ + struct ieee80211_mesh_hwmp_perr *perr_elem = (void *)pos; + u8 number_of_dst; + u8 needed; + const u8 *start; + int i; + + start = pos; + needed = sizeof(struct ieee80211_mesh_hwmp_perr); + pos += sizeof(struct ieee80211_mesh_hwmp_perr); + + /* Check if the element contains number of dst */ + if (elen < needed) + return false; + + number_of_dst = perr_elem->number_of_dst; + if (number_of_dst < 1 || number_of_dst > 19) + return false; + + for (i = 0; i < number_of_dst; i++) { + struct ieee80211_mesh_hwmp_perr_dst *perr_dst = + &perr_elem->dsts[i]; + u8 dst_len; + + /* Check if the element contains flags */ + if (elen < pos - start + 1) + return false; + + dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst) + + ((perr_dst->flags & AE_F) ? ETH_ALEN : 0) + /* Destination External Address */ + + 2 /* Reason Code */; + needed += dst_len; + pos += dst_len; + } + + return elen == needed; +} + #endif /* LINUX_IEEE80211_MESH_H */ diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 391d37721b23..a74d7b28a35d 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -957,9 +957,19 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, path_metric); } if (elems->perr) { - if (elems->perr_len != 15) - /* Right now we support only one destination per PERR */ + struct ieee80211_mesh_hwmp_perr *perr_elem = + (struct ieee80211_mesh_hwmp_perr *)elems->perr; + int i; + + /* Right now we support only one destination per PERR */ + if (perr_elem->number_of_dst != 1) goto free; + + /* Right now we do not support AE (Address Extension) */ + for (i = 0; i < perr_elem->number_of_dst; i++) + if (perr_elem->dsts[i].flags & AE_F) + goto free; + hwmp_perr_frame_process(sdata, mgmt, elems->perr); } if (elems->rann) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index bbd1e1bc77b4..d84e5e12ad24 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -565,8 +565,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, } break; case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; + if (ieee80211_mesh_perr_size_ok(pos, elen)) { + elems->perr = pos; + elems->perr_len = elen; + } else { + elem_parse_failed = + IEEE80211_PARSE_ERR_BAD_ELEM_SIZE; + } break; case WLAN_EID_RANN: if (elen >= sizeof(struct ieee80211_rann_ie)) -- 2.43.0