6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Bernard Pidoux When the last route using a neighbour is deleted (SIOCDELRT) or all routes are cleared (SIOCRSCLRRT), rose_del_node() and rose_clear_routes() take the neighbour out of rose_neigh_list, but the through routes in rose_route_list that still use it are left alone. No frame can reach them any more, so they stay in the table for ever, holding their neighbour references, and neither end of the relayed connection is told: the caller and the called station keep a circuit that goes nowhere. rose_clear_routes() even says it keeps neighbours with active connections, which it does not. This shows up on every node that relays traffic when its routing daemon restarts: FPAC clears and reloads the routes, the neighbours are recreated under new numbers, and the old through routes remain listed with neighbours that no longer exist. Clear them when the neighbour is removed, as rose_link_failed() does when a link fails, but send the CLEAR to both halves since the removed neighbour's link may still be up, and cut the local connections using it. rose_route_list_lock is taken under rose_node_list_lock and rose_neigh_list_lock, the order rose_route_frame() and rose_link_failed() already use. Tested in QEMU with a lockdep and KASAN kernel, three nodes A - B - C with A calling C through B: before, the through route stays on B after SIOCDELRT or SIOCRSCLRRT and A is never told; after, the entry is gone and both A and C get a CLEAR (cause 0x09) within a second. No lockdep, KASAN or refcount report. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bernard Pidoux (cherry picked from commit 065ae03f519c147d5ee7b87acb2d21fe93ae0007) Signed-off-by: Sasha Levin --- net/rose/rose_route.c | 49 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index fd196f2e2fd64..87844cc195f8d 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -285,6 +285,49 @@ static void rose_remove_route(struct rose_route *rose_route) } } +/* + * A neighbour is being removed from the tables while its link may still + * be up (route deleted or routes cleared by the configuration). Clear the + * through routes using it, on both sides since both can still be reached, + * instead of leaving them in rose_route_list for ever: no frame can reach + * them once the neighbour is gone. Local connections using it are cut too, + * as rose_link_failed() does. + * + * Caller is holding rose_node_list_lock and rose_neigh_list_lock, taken + * before rose_route_list_lock as in rose_route_frame() and + * rose_link_failed(). + */ +static void rose_neigh_clear_routes(struct rose_neigh *rose_neigh) +{ + struct rose_route *rose_route, *s; + + spin_lock_bh(&rose_route_list_lock); + + rose_route = rose_route_list; + while (rose_route != NULL) { + s = rose_route->next; + + if (rose_route->neigh1 == rose_neigh || + rose_route->neigh2 == rose_neigh) { + if (rose_route->neigh1 != NULL) + rose_transmit_clear_request(rose_route->neigh1, + rose_route->lci1, + ROSE_OUT_OF_ORDER, 0); + if (rose_route->neigh2 != NULL) + rose_transmit_clear_request(rose_route->neigh2, + rose_route->lci2, + ROSE_OUT_OF_ORDER, 0); + rose_remove_route(rose_route); + } + + rose_route = s; + } + + spin_unlock_bh(&rose_route_list_lock); + + rose_kill_by_neigh(rose_neigh); +} + /* * "Delete" a node. Strictly speaking remove a route to a node. The node * is only deleted if no routes are left to it. @@ -333,6 +376,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, rose_neigh_put(rose_neigh); if (rose_neigh->count == 0) { + rose_neigh_clear_routes(rose_neigh); rose_remove_neigh(rose_neigh); rose_neigh_put(rose_neigh); } @@ -547,8 +591,8 @@ void rose_route_device_down(struct net_device *dev) #endif /* - * Clear all nodes and neighbours out, except for neighbours with - * active connections going through them. + * Clear all nodes and neighbours out, with the through routes and + * connections using those neighbours. * Do not clear loopback neighbour and nodes. */ static int rose_clear_routes(void) @@ -579,6 +623,7 @@ static int rose_clear_routes(void) rose_neigh = rose_neigh->next; if (!s->loopback) { + rose_neigh_clear_routes(s); rose_remove_neigh(s); rose_neigh_put(s); } -- 2.53.0