Brief change list:
- Do more fine-grained locking: call eventhandlers/free_entry without holding afdata wlock
- convert per-af delete_address callback to global lltable_delete_entry() and more low-level "delete this lle" per-af callback
- fix some bugs/inconsistencies in IPv4/IPv6 ifscrub procedures
Currently we have 3 control inputs:
- user issuing delete via rtsock
- interface address being removed
- interface going down
Changes:
Case 1 (arp -d .. / ndp -c ..):
Old behavior:
Handled by lla_delete (calls per-af in[6]_lltable_delete()) which
grabs afdata wlock finds entry (ignore ifaddr ones) marks deleted, call eventhandler calls free_entry releases afdata wlock
Problems:
doing too much under afdata wlock
New behavior:
Handled by global lltable_delete_entry function which
grabs afdata wlock finds entry (ignore ifaddr ones) unlinks entry releases afdata wlock calls llt_delete_entry() cb which marks deleted, calls eventhandler calls free_entry
Case 2 (deleting interface address):
Old behavior (IPv4):
delete actual address in arp_ifscrub() under afdata wlock See if this particular prefix exist elsewhere, do not delete any lle if true
Problems:
In most common case (single prefix, no aliases) you acquire afdata wlock twice. Suppose you have 10.0.0.1/24 on em1 and 10.0.0.1/24 on em0 (currently active, e.g have route) then you removes 10.0.0.1 from em0 - nothing (except 10.0.0.1 itself) will be cleared. Suppose you have 10.0.0.1/24 and 10.0.0.10/25 on em0. You removes 10.0.0.1 address - 10.0.0.10 iface entry is cleared, too
New behavior (IPv4):
Check if we really need to flush all prefix lles. Regardless of the result, call in_scrubprefixlle() which performs either prefix or single address deletion.
Since lltable_match_prefix callbacks were modified to receive actual @ia address instead of prefix, lltable_prefix_free is now capable of deleting single LLE_IFADDR lle corresponding to @ia address.
Old behavior (IPv6):
in6_purgeaddr() call nd6_rem_ifa_lle() which deletes ALL prefix entries including LLE_IFADDR ones. After that, SIOCDIFADDR_IN6 handling logic checks if prefix refcount is 0, calls prelist_remove() if yes. prelist_remove() would call nd6_prefix_offlink(), it would check if prefix exists elsewhere and finally call lltable_prefix_free() if not.
New behavior(IPv6):
Pospone nd6_rem_ifa_lle() call till in6_unlink_ifa(), where we already know if it was the last address within prefix. Delete all dynamic/static prefix entries and LLE_IFADDR entry corresponding to @ifa address if true, delete matching address only otherwise.
Case 3 (interface going down):
No changes here.
We have in_scrubprefix() called from rip_ctlinput() for
IPv4 part which kills all dynamic entries and interface
prefix routes. We have nothing for IPv6 case.