mac802154: Handle received BEACON_REQ

When performing an active scan, devices emit BEACON_REQ which
must be answered by other PANs receiving the request, unless they are
already passively sending beacons.

Answering a beacon request becomes a duty when the user tells us to send
beacons and the request provides an interval of 15.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Alexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/r/20230310145346.1397068-5-miquel.raynal@bootlin.com
Signed-off-by: Stefan Schmidt <stefan@datenfreihafen.org>
This commit is contained in:
Miquel Raynal 2023-03-10 15:53:46 +01:00 committed by Stefan Schmidt
parent 26f88e4ebd
commit d021d218f6
6 changed files with 117 additions and 5 deletions

View File

@ -193,6 +193,8 @@ int ieee802154_beacon_push(struct sk_buff *skb,
struct ieee802154_beacon_frame *beacon);
int ieee802154_mac_cmd_push(struct sk_buff *skb, void *frame,
const void *pl, unsigned int pl_len);
int ieee802154_mac_cmd_pl_pull(struct sk_buff *skb,
struct ieee802154_mac_cmd_pl *mac_pl);
int ieee802154_max_payload(const struct ieee802154_hdr *hdr);

View File

@ -307,6 +307,19 @@ ieee802154_hdr_pull(struct sk_buff *skb, struct ieee802154_hdr *hdr)
}
EXPORT_SYMBOL_GPL(ieee802154_hdr_pull);
int ieee802154_mac_cmd_pl_pull(struct sk_buff *skb,
struct ieee802154_mac_cmd_pl *mac_pl)
{
if (!pskb_may_pull(skb, sizeof(*mac_pl)))
return -EINVAL;
memcpy(mac_pl, skb->data, sizeof(*mac_pl));
skb_pull(skb, sizeof(*mac_pl));
return 0;
}
EXPORT_SYMBOL_GPL(ieee802154_mac_cmd_pl_pull);
int
ieee802154_hdr_peek_addrs(const struct sk_buff *skb, struct ieee802154_hdr *hdr)
{

View File

@ -71,6 +71,8 @@ struct ieee802154_local {
/* Asynchronous tasks */
struct list_head rx_beacon_list;
struct work_struct rx_beacon_work;
struct list_head rx_mac_cmd_list;
struct work_struct rx_mac_cmd_work;
bool started;
bool suspended;
@ -155,6 +157,22 @@ ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
}
static inline int ieee802154_get_mac_cmd(struct sk_buff *skb, u8 *mac_cmd)
{
struct ieee802154_mac_cmd_pl mac_pl;
int ret;
if (mac_cb(skb)->type != IEEE802154_FC_TYPE_MAC_CMD)
return -EINVAL;
ret = ieee802154_mac_cmd_pl_pull(skb, &mac_pl);
if (ret)
return ret;
*mac_cmd = mac_pl.cmd_id;
return 0;
}
extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb);
@ -276,6 +294,8 @@ static inline bool mac802154_is_beaconing(struct ieee802154_local *local)
return test_bit(IEEE802154_IS_BEACONING, &local->ongoing);
}
void mac802154_rx_mac_cmd_worker(struct work_struct *work);
/* interface handling */
int ieee802154_iface_init(void);
void ieee802154_iface_exit(void);

View File

@ -90,6 +90,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
INIT_LIST_HEAD(&local->interfaces);
INIT_LIST_HEAD(&local->rx_beacon_list);
INIT_LIST_HEAD(&local->rx_mac_cmd_list);
mutex_init(&local->iflist_mtx);
tasklet_setup(&local->tasklet, ieee802154_tasklet_handler);
@ -100,6 +101,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
INIT_DELAYED_WORK(&local->scan_work, mac802154_scan_worker);
INIT_WORK(&local->rx_beacon_work, mac802154_rx_beacon_worker);
INIT_DELAYED_WORK(&local->beacon_work, mac802154_beacon_worker);
INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
/* init supported flags with 802.15.4 default ranges */
phy->supported.max_minbe = 8;

View File

@ -47,6 +47,62 @@ void mac802154_rx_beacon_worker(struct work_struct *work)
kfree(mac_pkt);
}
static bool mac802154_should_answer_beacon_req(struct ieee802154_local *local)
{
struct cfg802154_beacon_request *beacon_req;
unsigned int interval;
rcu_read_lock();
beacon_req = rcu_dereference(local->beacon_req);
if (!beacon_req) {
rcu_read_unlock();
return false;
}
interval = beacon_req->interval;
rcu_read_unlock();
if (!mac802154_is_beaconing(local))
return false;
return interval == IEEE802154_ACTIVE_SCAN_DURATION;
}
void mac802154_rx_mac_cmd_worker(struct work_struct *work)
{
struct ieee802154_local *local =
container_of(work, struct ieee802154_local, rx_mac_cmd_work);
struct cfg802154_mac_pkt *mac_pkt;
u8 mac_cmd;
int rc;
mac_pkt = list_first_entry_or_null(&local->rx_mac_cmd_list,
struct cfg802154_mac_pkt, node);
if (!mac_pkt)
return;
rc = ieee802154_get_mac_cmd(mac_pkt->skb, &mac_cmd);
if (rc)
goto out;
switch (mac_cmd) {
case IEEE802154_CMD_BEACON_REQ:
dev_dbg(&mac_pkt->sdata->dev->dev, "processing BEACON REQ\n");
if (!mac802154_should_answer_beacon_req(local))
break;
queue_delayed_work(local->mac_wq, &local->beacon_work, 0);
break;
default:
break;
}
out:
list_del(&mac_pkt->node);
kfree_skb(mac_pkt->skb);
kfree(mac_pkt);
}
static int
ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
struct sk_buff *skb, const struct ieee802154_hdr *hdr)
@ -140,8 +196,20 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
list_add_tail(&mac_pkt->node, &sdata->local->rx_beacon_list);
queue_work(sdata->local->mac_wq, &sdata->local->rx_beacon_work);
return NET_RX_SUCCESS;
case IEEE802154_FC_TYPE_ACK:
case IEEE802154_FC_TYPE_MAC_CMD:
dev_dbg(&sdata->dev->dev, "MAC COMMAND received\n");
mac_pkt = kzalloc(sizeof(*mac_pkt), GFP_ATOMIC);
if (!mac_pkt)
goto fail;
mac_pkt->skb = skb_get(skb);
mac_pkt->sdata = sdata;
list_add_tail(&mac_pkt->node, &sdata->local->rx_mac_cmd_list);
queue_work(sdata->local->mac_wq, &sdata->local->rx_mac_cmd_work);
return NET_RX_SUCCESS;
case IEEE802154_FC_TYPE_ACK:
goto fail;
case IEEE802154_FC_TYPE_DATA:

View File

@ -403,6 +403,7 @@ void mac802154_beacon_worker(struct work_struct *work)
struct cfg802154_beacon_request *beacon_req;
struct ieee802154_sub_if_data *sdata;
struct wpan_dev *wpan_dev;
u8 interval;
int ret;
rcu_read_lock();
@ -423,6 +424,7 @@ void mac802154_beacon_worker(struct work_struct *work)
}
wpan_dev = beacon_req->wpan_dev;
interval = beacon_req->interval;
rcu_read_unlock();
@ -432,8 +434,9 @@ void mac802154_beacon_worker(struct work_struct *work)
dev_err(&sdata->dev->dev,
"Beacon could not be transmitted (%d)\n", ret);
queue_delayed_work(local->mac_wq, &local->beacon_work,
local->beacon_interval);
if (interval < IEEE802154_ACTIVE_SCAN_DURATION)
queue_delayed_work(local->mac_wq, &local->beacon_work,
local->beacon_interval);
}
int mac802154_stop_beacons_locked(struct ieee802154_local *local,
@ -488,13 +491,17 @@ int mac802154_send_beacons_locked(struct ieee802154_sub_if_data *sdata,
local->beacon.mhr.source.pan_id = request->wpan_dev->pan_id;
local->beacon.mhr.source.extended_addr = request->wpan_dev->extended_addr;
local->beacon.mac_pl.beacon_order = request->interval;
local->beacon.mac_pl.superframe_order = request->interval;
if (request->interval <= IEEE802154_MAX_SCAN_DURATION)
local->beacon.mac_pl.superframe_order = request->interval;
local->beacon.mac_pl.final_cap_slot = 0xf;
local->beacon.mac_pl.battery_life_ext = 0;
/* TODO: Fill this field depending on the coordinator capacity */
/* TODO: Fill this field with the coordinator situation in the network */
local->beacon.mac_pl.pan_coordinator = 1;
local->beacon.mac_pl.assoc_permit = 1;
if (request->interval == IEEE802154_ACTIVE_SCAN_DURATION)
return 0;
/* Start the beacon work */
local->beacon_interval =
mac802154_scan_get_channel_time(request->interval,