Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac802154: Handle disassociations

Devices may decide to disassociate from their coordinator for different
reasons (device turning off, coordinator signal strength too low, etc),
the MAC layer just has to send a disassociation notification.

If the ack of the disassociation notification is not received, the
device may consider itself disassociated anyway.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Stefan Schmidt <stefan@datenfreihafen.org>
Acked-by: Alexander Aring <aahringo@redhat.com>
Link: https://lore.kernel.org/linux-wpan/20230927181214.129346-7-miquel.raynal@bootlin.com

+166
+2
net/ieee802154/pan.c
··· 46 46 47 47 return cfg802154_pan_device_is_matching(wpan_dev->parent, target); 48 48 } 49 + EXPORT_SYMBOL_GPL(cfg802154_device_is_parent); 49 50 50 51 struct ieee802154_pan_device * 51 52 cfg802154_device_is_child(struct wpan_dev *wpan_dev, ··· 62 61 63 62 return NULL; 64 63 } 64 + EXPORT_SYMBOL_GPL(cfg802154_device_is_child);
+100
net/mac802154/cfg.c
··· 383 383 return ret; 384 384 } 385 385 386 + static int mac802154_disassociate_from_parent(struct wpan_phy *wpan_phy, 387 + struct wpan_dev *wpan_dev) 388 + { 389 + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); 390 + struct ieee802154_pan_device *child, *tmp; 391 + struct ieee802154_sub_if_data *sdata; 392 + u64 eaddr; 393 + int ret; 394 + 395 + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev); 396 + 397 + /* Start by disassociating all the children and preventing new ones to 398 + * attempt associations. 399 + */ 400 + list_for_each_entry_safe(child, tmp, &wpan_dev->children, node) { 401 + ret = mac802154_send_disassociation_notif(sdata, child, 402 + IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE); 403 + if (ret) { 404 + eaddr = swab64((__force u64)child->extended_addr); 405 + dev_err(&sdata->dev->dev, 406 + "Disassociation with %8phC may have failed (%d)\n", 407 + &eaddr, ret); 408 + } 409 + 410 + list_del(&child->node); 411 + } 412 + 413 + ret = mac802154_send_disassociation_notif(sdata, wpan_dev->parent, 414 + IEEE802154_DEVICE_WISHES_TO_LEAVE); 415 + if (ret) { 416 + eaddr = swab64((__force u64)wpan_dev->parent->extended_addr); 417 + dev_err(&sdata->dev->dev, 418 + "Disassociation from %8phC may have failed (%d)\n", 419 + &eaddr, ret); 420 + } 421 + 422 + ret = 0; 423 + 424 + kfree(wpan_dev->parent); 425 + wpan_dev->parent = NULL; 426 + wpan_dev->pan_id = cpu_to_le16(IEEE802154_PAN_ID_BROADCAST); 427 + wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST); 428 + 429 + if (local->hw.flags & IEEE802154_HW_AFILT) { 430 + ret = drv_set_pan_id(local, wpan_dev->pan_id); 431 + if (ret < 0) 432 + return ret; 433 + 434 + ret = drv_set_short_addr(local, wpan_dev->short_addr); 435 + if (ret < 0) 436 + return ret; 437 + } 438 + 439 + return 0; 440 + } 441 + 442 + static int mac802154_disassociate_child(struct wpan_phy *wpan_phy, 443 + struct wpan_dev *wpan_dev, 444 + struct ieee802154_pan_device *child) 445 + { 446 + struct ieee802154_sub_if_data *sdata; 447 + int ret; 448 + 449 + sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev); 450 + 451 + ret = mac802154_send_disassociation_notif(sdata, child, 452 + IEEE802154_COORD_WISHES_DEVICE_TO_LEAVE); 453 + if (ret) 454 + return ret; 455 + 456 + list_del(&child->node); 457 + kfree(child); 458 + 459 + return 0; 460 + } 461 + 462 + static int mac802154_disassociate(struct wpan_phy *wpan_phy, 463 + struct wpan_dev *wpan_dev, 464 + struct ieee802154_addr *target) 465 + { 466 + u64 teaddr = swab64((__force u64)target->extended_addr); 467 + struct ieee802154_pan_device *pan_device; 468 + 469 + ASSERT_RTNL(); 470 + 471 + if (cfg802154_device_is_parent(wpan_dev, target)) 472 + return mac802154_disassociate_from_parent(wpan_phy, wpan_dev); 473 + 474 + pan_device = cfg802154_device_is_child(wpan_dev, target); 475 + if (pan_device) 476 + return mac802154_disassociate_child(wpan_phy, wpan_dev, 477 + pan_device); 478 + 479 + dev_err(&wpan_dev->netdev->dev, 480 + "Device %8phC is not associated with us\n", &teaddr); 481 + 482 + return -EINVAL; 483 + } 484 + 386 485 #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 387 486 static void 388 487 ieee802154_get_llsec_table(struct wpan_phy *wpan_phy, ··· 694 595 .send_beacons = mac802154_send_beacons, 695 596 .stop_beacons = mac802154_stop_beacons, 696 597 .associate = mac802154_associate, 598 + .disassociate = mac802154_disassociate, 697 599 #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 698 600 .get_llsec_table = ieee802154_get_llsec_table, 699 601 .lock_llsec_table = ieee802154_lock_llsec_table,
+4
net/mac802154/ieee802154_i.h
··· 315 315 return test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing); 316 316 } 317 317 318 + int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, 319 + struct ieee802154_pan_device *target, 320 + u8 reason); 321 + 318 322 /* interface handling */ 319 323 int ieee802154_iface_init(void); 320 324 void ieee802154_iface_exit(void);
+60
net/mac802154/scan.c
··· 637 637 638 638 return 0; 639 639 } 640 + 641 + int mac802154_send_disassociation_notif(struct ieee802154_sub_if_data *sdata, 642 + struct ieee802154_pan_device *target, 643 + u8 reason) 644 + { 645 + struct ieee802154_disassociation_notif_frame frame = {}; 646 + u64 teaddr = swab64((__force u64)target->extended_addr); 647 + struct ieee802154_local *local = sdata->local; 648 + struct wpan_dev *wpan_dev = &sdata->wpan_dev; 649 + struct sk_buff *skb; 650 + int ret; 651 + 652 + frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD; 653 + frame.mhr.fc.security_enabled = 0; 654 + frame.mhr.fc.frame_pending = 0; 655 + frame.mhr.fc.ack_request = 1; 656 + frame.mhr.fc.intra_pan = 1; 657 + frame.mhr.fc.dest_addr_mode = (target->mode == IEEE802154_ADDR_LONG) ? 658 + IEEE802154_EXTENDED_ADDRESSING : IEEE802154_SHORT_ADDRESSING; 659 + frame.mhr.fc.version = IEEE802154_2003_STD; 660 + frame.mhr.fc.source_addr_mode = IEEE802154_EXTENDED_ADDRESSING; 661 + frame.mhr.source.mode = IEEE802154_ADDR_LONG; 662 + frame.mhr.source.pan_id = wpan_dev->pan_id; 663 + frame.mhr.source.extended_addr = wpan_dev->extended_addr; 664 + frame.mhr.dest.mode = target->mode; 665 + frame.mhr.dest.pan_id = wpan_dev->pan_id; 666 + if (target->mode == IEEE802154_ADDR_LONG) 667 + frame.mhr.dest.extended_addr = target->extended_addr; 668 + else 669 + frame.mhr.dest.short_addr = target->short_addr; 670 + frame.mhr.seq = atomic_inc_return(&wpan_dev->dsn) & 0xFF; 671 + frame.mac_pl.cmd_id = IEEE802154_CMD_DISASSOCIATION_NOTIFY; 672 + frame.disassoc_pl = reason; 673 + 674 + skb = alloc_skb(IEEE802154_MAC_CMD_SKB_SZ + sizeof(frame.disassoc_pl), 675 + GFP_KERNEL); 676 + if (!skb) 677 + return -ENOBUFS; 678 + 679 + skb->dev = sdata->dev; 680 + 681 + ret = ieee802154_mac_cmd_push(skb, &frame, &frame.disassoc_pl, 682 + sizeof(frame.disassoc_pl)); 683 + if (ret) { 684 + kfree_skb(skb); 685 + return ret; 686 + } 687 + 688 + ret = ieee802154_mlme_tx_one_locked(local, sdata, skb); 689 + if (ret) { 690 + dev_warn(&sdata->dev->dev, 691 + "No DISASSOC ACK received from %8phC\n", &teaddr); 692 + if (ret > 0) 693 + ret = (ret == IEEE802154_NO_ACK) ? -EREMOTEIO : -EIO; 694 + return ret; 695 + } 696 + 697 + dev_dbg(&sdata->dev->dev, "DISASSOC ACK received from %8phC\n", &teaddr); 698 + return 0; 699 + }