| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2008, 2009 open80211s Ltd. |
|---|
| 4 | + * Copyright (C) 2019 Intel Corporation |
|---|
| 3 | 5 | * Author: Luis Carlos Cobo <luisca@cozybit.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | | - * published by the Free Software Foundation. |
|---|
| 8 | 6 | */ |
|---|
| 9 | 7 | |
|---|
| 10 | 8 | #include <linux/slab.h> |
|---|
| .. | .. |
|---|
| 214 | 212 | skb->priority = 7; |
|---|
| 215 | 213 | |
|---|
| 216 | 214 | info->control.vif = &sdata->vif; |
|---|
| 217 | | - info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; |
|---|
| 215 | + info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING; |
|---|
| 218 | 216 | ieee80211_set_qos_hdr(sdata, skb); |
|---|
| 219 | 217 | ieee80211_mps_set_frame_flags(sdata, NULL, hdr); |
|---|
| 220 | 218 | } |
|---|
| .. | .. |
|---|
| 300 | 298 | { |
|---|
| 301 | 299 | struct ieee80211_tx_info *txinfo = st->info; |
|---|
| 302 | 300 | int failed; |
|---|
| 301 | + struct rate_info rinfo; |
|---|
| 303 | 302 | |
|---|
| 304 | 303 | failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); |
|---|
| 305 | 304 | |
|---|
| .. | .. |
|---|
| 310 | 309 | if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > |
|---|
| 311 | 310 | LINK_FAIL_THRESH) |
|---|
| 312 | 311 | mesh_plink_broken(sta); |
|---|
| 312 | + |
|---|
| 313 | + sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); |
|---|
| 314 | + ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, |
|---|
| 315 | + cfg80211_calculate_bitrate(&rinfo)); |
|---|
| 313 | 316 | } |
|---|
| 314 | 317 | |
|---|
| 315 | | -static u32 airtime_link_metric_get(struct ieee80211_local *local, |
|---|
| 316 | | - struct sta_info *sta) |
|---|
| 318 | +u32 airtime_link_metric_get(struct ieee80211_local *local, |
|---|
| 319 | + struct sta_info *sta) |
|---|
| 317 | 320 | { |
|---|
| 318 | | - struct rate_info rinfo; |
|---|
| 319 | 321 | /* This should be adjusted for each device */ |
|---|
| 320 | 322 | int device_constant = 1 << ARITH_SHIFT; |
|---|
| 321 | 323 | int test_frame_len = TEST_FRAME_LEN << ARITH_SHIFT; |
|---|
| .. | .. |
|---|
| 342 | 344 | if (fail_avg > LINK_FAIL_THRESH) |
|---|
| 343 | 345 | return MAX_METRIC; |
|---|
| 344 | 346 | |
|---|
| 345 | | - sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); |
|---|
| 346 | | - rate = cfg80211_calculate_bitrate(&rinfo); |
|---|
| 347 | + rate = ewma_mesh_tx_rate_avg_read(&sta->mesh->tx_rate_avg); |
|---|
| 347 | 348 | if (WARN_ON(!rate)) |
|---|
| 348 | 349 | return MAX_METRIC; |
|---|
| 349 | 350 | |
|---|
| .. | .. |
|---|
| 389 | 390 | unsigned long orig_lifetime, exp_time; |
|---|
| 390 | 391 | u32 last_hop_metric, new_metric; |
|---|
| 391 | 392 | bool process = true; |
|---|
| 393 | + u8 hopcount; |
|---|
| 392 | 394 | |
|---|
| 393 | 395 | rcu_read_lock(); |
|---|
| 394 | 396 | sta = sta_info_get(sdata, mgmt->sa); |
|---|
| .. | .. |
|---|
| 407 | 409 | orig_sn = PREQ_IE_ORIG_SN(hwmp_ie); |
|---|
| 408 | 410 | orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie); |
|---|
| 409 | 411 | orig_metric = PREQ_IE_METRIC(hwmp_ie); |
|---|
| 412 | + hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1; |
|---|
| 410 | 413 | break; |
|---|
| 411 | 414 | case MPATH_PREP: |
|---|
| 412 | 415 | /* Originator here refers to the MP that was the target in the |
|---|
| .. | .. |
|---|
| 418 | 421 | orig_sn = PREP_IE_TARGET_SN(hwmp_ie); |
|---|
| 419 | 422 | orig_lifetime = PREP_IE_LIFETIME(hwmp_ie); |
|---|
| 420 | 423 | orig_metric = PREP_IE_METRIC(hwmp_ie); |
|---|
| 424 | + hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1; |
|---|
| 421 | 425 | break; |
|---|
| 422 | 426 | default: |
|---|
| 423 | 427 | rcu_read_unlock(); |
|---|
| .. | .. |
|---|
| 444 | 448 | (mpath->flags & MESH_PATH_SN_VALID)) { |
|---|
| 445 | 449 | if (SN_GT(mpath->sn, orig_sn) || |
|---|
| 446 | 450 | (mpath->sn == orig_sn && |
|---|
| 447 | | - new_metric >= mpath->metric)) { |
|---|
| 451 | + (rcu_access_pointer(mpath->next_hop) != |
|---|
| 452 | + sta ? |
|---|
| 453 | + mult_frac(new_metric, 10, 9) : |
|---|
| 454 | + new_metric) >= mpath->metric)) { |
|---|
| 448 | 455 | process = false; |
|---|
| 449 | 456 | fresh_info = false; |
|---|
| 450 | 457 | } |
|---|
| .. | .. |
|---|
| 479 | 486 | } |
|---|
| 480 | 487 | |
|---|
| 481 | 488 | if (fresh_info) { |
|---|
| 489 | + if (rcu_access_pointer(mpath->next_hop) != sta) |
|---|
| 490 | + mpath->path_change_count++; |
|---|
| 482 | 491 | mesh_path_assign_nexthop(mpath, sta); |
|---|
| 483 | 492 | mpath->flags |= MESH_PATH_SN_VALID; |
|---|
| 484 | 493 | mpath->metric = new_metric; |
|---|
| 485 | 494 | mpath->sn = orig_sn; |
|---|
| 486 | 495 | mpath->exp_time = time_after(mpath->exp_time, exp_time) |
|---|
| 487 | 496 | ? mpath->exp_time : exp_time; |
|---|
| 497 | + mpath->hop_count = hopcount; |
|---|
| 488 | 498 | mesh_path_activate(mpath); |
|---|
| 489 | 499 | spin_unlock_bh(&mpath->state_lock); |
|---|
| 490 | 500 | ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); |
|---|
| .. | .. |
|---|
| 509 | 519 | if (mpath) { |
|---|
| 510 | 520 | spin_lock_bh(&mpath->state_lock); |
|---|
| 511 | 521 | if ((mpath->flags & MESH_PATH_FIXED) || |
|---|
| 512 | | - ((mpath->flags & MESH_PATH_ACTIVE) && |
|---|
| 513 | | - (last_hop_metric > mpath->metric))) |
|---|
| 522 | + ((mpath->flags & MESH_PATH_ACTIVE) && |
|---|
| 523 | + ((rcu_access_pointer(mpath->next_hop) != sta ? |
|---|
| 524 | + mult_frac(last_hop_metric, 10, 9) : |
|---|
| 525 | + last_hop_metric) > mpath->metric))) |
|---|
| 514 | 526 | fresh_info = false; |
|---|
| 515 | 527 | } else { |
|---|
| 516 | 528 | mpath = mesh_path_add(sdata, ta); |
|---|
| .. | .. |
|---|
| 522 | 534 | } |
|---|
| 523 | 535 | |
|---|
| 524 | 536 | if (fresh_info) { |
|---|
| 537 | + if (rcu_access_pointer(mpath->next_hop) != sta) |
|---|
| 538 | + mpath->path_change_count++; |
|---|
| 525 | 539 | mesh_path_assign_nexthop(mpath, sta); |
|---|
| 526 | 540 | mpath->metric = last_hop_metric; |
|---|
| 527 | 541 | mpath->exp_time = time_after(mpath->exp_time, exp_time) |
|---|
| 528 | 542 | ? mpath->exp_time : exp_time; |
|---|
| 543 | + mpath->hop_count = 1; |
|---|
| 529 | 544 | mesh_path_activate(mpath); |
|---|
| 530 | 545 | spin_unlock_bh(&mpath->state_lock); |
|---|
| 531 | 546 | ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); |
|---|
| .. | .. |
|---|
| 912 | 927 | |
|---|
| 913 | 928 | baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; |
|---|
| 914 | 929 | ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, |
|---|
| 915 | | - len - baselen, false, &elems); |
|---|
| 930 | + len - baselen, false, &elems, mgmt->bssid, NULL); |
|---|
| 916 | 931 | |
|---|
| 917 | 932 | if (elems.preq) { |
|---|
| 918 | 933 | if (elems.preq_len != 37) |
|---|
| .. | .. |
|---|
| 1090 | 1105 | ttl, lifetime, 0, ifmsh->preq_id++, sdata); |
|---|
| 1091 | 1106 | |
|---|
| 1092 | 1107 | spin_lock_bh(&mpath->state_lock); |
|---|
| 1093 | | - if (mpath->flags & MESH_PATH_DELETED) { |
|---|
| 1094 | | - spin_unlock_bh(&mpath->state_lock); |
|---|
| 1095 | | - goto enddiscovery; |
|---|
| 1096 | | - } |
|---|
| 1097 | | - mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout); |
|---|
| 1108 | + if (!(mpath->flags & MESH_PATH_DELETED)) |
|---|
| 1109 | + mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout); |
|---|
| 1098 | 1110 | spin_unlock_bh(&mpath->state_lock); |
|---|
| 1099 | 1111 | |
|---|
| 1100 | 1112 | enddiscovery: |
|---|
| .. | .. |
|---|
| 1122 | 1134 | struct mesh_path *mpath; |
|---|
| 1123 | 1135 | struct sk_buff *skb_to_free = NULL; |
|---|
| 1124 | 1136 | u8 *target_addr = hdr->addr3; |
|---|
| 1125 | | - int err = 0; |
|---|
| 1126 | 1137 | |
|---|
| 1127 | 1138 | /* Nulls are only sent to peers for PS and should be pre-addressed */ |
|---|
| 1128 | 1139 | if (ieee80211_is_qos_nullfunc(hdr->frame_control)) |
|---|
| 1129 | 1140 | return 0; |
|---|
| 1130 | 1141 | |
|---|
| 1131 | | - rcu_read_lock(); |
|---|
| 1132 | | - err = mesh_nexthop_lookup(sdata, skb); |
|---|
| 1133 | | - if (!err) |
|---|
| 1134 | | - goto endlookup; |
|---|
| 1142 | + /* Allow injected packets to bypass mesh routing */ |
|---|
| 1143 | + if (info->control.flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP) |
|---|
| 1144 | + return 0; |
|---|
| 1145 | + |
|---|
| 1146 | + if (!mesh_nexthop_lookup(sdata, skb)) |
|---|
| 1147 | + return 0; |
|---|
| 1135 | 1148 | |
|---|
| 1136 | 1149 | /* no nexthop found, start resolving */ |
|---|
| 1137 | 1150 | mpath = mesh_path_lookup(sdata, target_addr); |
|---|
| .. | .. |
|---|
| 1139 | 1152 | mpath = mesh_path_add(sdata, target_addr); |
|---|
| 1140 | 1153 | if (IS_ERR(mpath)) { |
|---|
| 1141 | 1154 | mesh_path_discard_frame(sdata, skb); |
|---|
| 1142 | | - err = PTR_ERR(mpath); |
|---|
| 1143 | | - goto endlookup; |
|---|
| 1155 | + return PTR_ERR(mpath); |
|---|
| 1144 | 1156 | } |
|---|
| 1145 | 1157 | } |
|---|
| 1146 | 1158 | |
|---|
| .. | .. |
|---|
| 1151 | 1163 | if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) |
|---|
| 1152 | 1164 | skb_to_free = skb_dequeue(&mpath->frame_queue); |
|---|
| 1153 | 1165 | |
|---|
| 1154 | | - info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; |
|---|
| 1166 | + info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING; |
|---|
| 1155 | 1167 | ieee80211_set_qos_hdr(sdata, skb); |
|---|
| 1156 | 1168 | skb_queue_tail(&mpath->frame_queue, skb); |
|---|
| 1157 | | - err = -ENOENT; |
|---|
| 1158 | 1169 | if (skb_to_free) |
|---|
| 1159 | 1170 | mesh_path_discard_frame(sdata, skb_to_free); |
|---|
| 1160 | 1171 | |
|---|
| 1161 | | -endlookup: |
|---|
| 1172 | + return -ENOENT; |
|---|
| 1173 | +} |
|---|
| 1174 | + |
|---|
| 1175 | +/** |
|---|
| 1176 | + * mesh_nexthop_lookup_nolearn - try to set next hop without path discovery |
|---|
| 1177 | + * @skb: 802.11 frame to be sent |
|---|
| 1178 | + * @sdata: network subif the frame will be sent through |
|---|
| 1179 | + * |
|---|
| 1180 | + * Check if the meshDA (addr3) of a unicast frame is a direct neighbor. |
|---|
| 1181 | + * And if so, set the RA (addr1) to it to transmit to this node directly, |
|---|
| 1182 | + * avoiding PREQ/PREP path discovery. |
|---|
| 1183 | + * |
|---|
| 1184 | + * Returns: 0 if the next hop was found and -ENOENT otherwise. |
|---|
| 1185 | + */ |
|---|
| 1186 | +static int mesh_nexthop_lookup_nolearn(struct ieee80211_sub_if_data *sdata, |
|---|
| 1187 | + struct sk_buff *skb) |
|---|
| 1188 | +{ |
|---|
| 1189 | + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
|---|
| 1190 | + struct sta_info *sta; |
|---|
| 1191 | + |
|---|
| 1192 | + if (is_multicast_ether_addr(hdr->addr1)) |
|---|
| 1193 | + return -ENOENT; |
|---|
| 1194 | + |
|---|
| 1195 | + rcu_read_lock(); |
|---|
| 1196 | + sta = sta_info_get(sdata, hdr->addr3); |
|---|
| 1197 | + |
|---|
| 1198 | + if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) { |
|---|
| 1199 | + rcu_read_unlock(); |
|---|
| 1200 | + return -ENOENT; |
|---|
| 1201 | + } |
|---|
| 1162 | 1202 | rcu_read_unlock(); |
|---|
| 1163 | | - return err; |
|---|
| 1203 | + |
|---|
| 1204 | + memcpy(hdr->addr1, hdr->addr3, ETH_ALEN); |
|---|
| 1205 | + memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); |
|---|
| 1206 | + return 0; |
|---|
| 1164 | 1207 | } |
|---|
| 1165 | 1208 | |
|---|
| 1166 | 1209 | /** |
|---|
| .. | .. |
|---|
| 1176 | 1219 | int mesh_nexthop_lookup(struct ieee80211_sub_if_data *sdata, |
|---|
| 1177 | 1220 | struct sk_buff *skb) |
|---|
| 1178 | 1221 | { |
|---|
| 1222 | + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
|---|
| 1179 | 1223 | struct mesh_path *mpath; |
|---|
| 1180 | 1224 | struct sta_info *next_hop; |
|---|
| 1181 | 1225 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
|---|
| 1182 | 1226 | u8 *target_addr = hdr->addr3; |
|---|
| 1183 | | - int err = -ENOENT; |
|---|
| 1184 | 1227 | |
|---|
| 1185 | | - rcu_read_lock(); |
|---|
| 1228 | + if (ifmsh->mshcfg.dot11MeshNolearn && |
|---|
| 1229 | + !mesh_nexthop_lookup_nolearn(sdata, skb)) |
|---|
| 1230 | + return 0; |
|---|
| 1231 | + |
|---|
| 1186 | 1232 | mpath = mesh_path_lookup(sdata, target_addr); |
|---|
| 1187 | | - |
|---|
| 1188 | 1233 | if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE)) |
|---|
| 1189 | | - goto endlookup; |
|---|
| 1234 | + return -ENOENT; |
|---|
| 1190 | 1235 | |
|---|
| 1191 | 1236 | if (time_after(jiffies, |
|---|
| 1192 | 1237 | mpath->exp_time - |
|---|
| .. | .. |
|---|
| 1201 | 1246 | memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN); |
|---|
| 1202 | 1247 | memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); |
|---|
| 1203 | 1248 | ieee80211_mps_set_frame_flags(sdata, next_hop, hdr); |
|---|
| 1204 | | - err = 0; |
|---|
| 1249 | + return 0; |
|---|
| 1205 | 1250 | } |
|---|
| 1206 | 1251 | |
|---|
| 1207 | | -endlookup: |
|---|
| 1208 | | - rcu_read_unlock(); |
|---|
| 1209 | | - return err; |
|---|
| 1252 | + return -ENOENT; |
|---|
| 1210 | 1253 | } |
|---|
| 1211 | 1254 | |
|---|
| 1212 | 1255 | void mesh_path_timer(struct timer_list *t) |
|---|
| .. | .. |
|---|
| 1262 | 1305 | break; |
|---|
| 1263 | 1306 | case IEEE80211_PROACTIVE_PREQ_WITH_PREP: |
|---|
| 1264 | 1307 | flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG; |
|---|
| 1265 | | - /* fall through */ |
|---|
| 1308 | + fallthrough; |
|---|
| 1266 | 1309 | case IEEE80211_PROACTIVE_PREQ_NO_PREP: |
|---|
| 1267 | 1310 | interval = ifmsh->mshcfg.dot11MeshHWMPactivePathToRootTimeout; |
|---|
| 1268 | 1311 | target_flags |= IEEE80211_PREQ_TO_FLAG | |
|---|