mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-23 02:01:55 +00:00
ab00639130
When multiple protocols have a lock for the same IP address, it crashes under some circumstances. Thanks to Matthias Schiffer for the bugreport.
195 lines
4.9 KiB
C
195 lines
4.9 KiB
C
/*
|
|
* BIRD Object Locks
|
|
*
|
|
* (c) 1999 Martin Mares <mj@ucw.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
/**
|
|
* DOC: Object locks
|
|
*
|
|
* The lock module provides a simple mechanism for avoiding conflicts between
|
|
* various protocols which would like to use a single physical resource (for
|
|
* example a network port). It would be easy to say that such collisions can
|
|
* occur only when the user specifies an invalid configuration and therefore
|
|
* he deserves to get what he has asked for, but unfortunately they can also
|
|
* arise legitimately when the daemon is reconfigured and there exists (although
|
|
* for a short time period only) an old protocol instance being shut down and a new one
|
|
* willing to start up on the same interface.
|
|
*
|
|
* The solution is very simple: when any protocol wishes to use a network port
|
|
* or some other non-shareable resource, it asks the core to lock it and it doesn't
|
|
* use the resource until it's notified that it has acquired the lock.
|
|
*
|
|
* Object locks are represented by &object_lock structures which are in turn a
|
|
* kind of resource. Lockable resources are uniquely determined by resource type
|
|
* (%OBJLOCK_UDP for a UDP port etc.), IP address (usually a broadcast or
|
|
* multicast address the port is bound to), port number, interface and optional
|
|
* instance ID.
|
|
*/
|
|
|
|
#undef LOCAL_DEBUG
|
|
|
|
#include "nest/bird.h"
|
|
#include "lib/resource.h"
|
|
#include "nest/locks.h"
|
|
#include "nest/iface.h"
|
|
|
|
static list olock_list;
|
|
static event *olock_event;
|
|
|
|
static inline int
|
|
olock_same(struct object_lock *x, struct object_lock *y)
|
|
{
|
|
return
|
|
x->type == y->type &&
|
|
x->iface == y->iface &&
|
|
x->port == y->port &&
|
|
x->inst == y->inst &&
|
|
ipa_equal(x->addr, y->addr);
|
|
}
|
|
|
|
static void
|
|
olock_free(resource *r)
|
|
{
|
|
struct object_lock *q, *l = (struct object_lock *) r;
|
|
node *n;
|
|
|
|
DBG("olock: Freeing %p\n", l);
|
|
switch (l->state)
|
|
{
|
|
case OLOCK_STATE_FREE:
|
|
break;
|
|
case OLOCK_STATE_LOCKED:
|
|
case OLOCK_STATE_EVENT:
|
|
rem_node(&l->n);
|
|
n = HEAD(l->waiters);
|
|
if (n->next)
|
|
{
|
|
DBG("olock: -> %p becomes locked\n", n);
|
|
q = SKIP_BACK(struct object_lock, n, n);
|
|
rem_node(n);
|
|
add_tail_list(&q->waiters, &l->waiters);
|
|
q->state = OLOCK_STATE_EVENT;
|
|
add_head(&olock_list, n);
|
|
ev_schedule(olock_event);
|
|
}
|
|
break;
|
|
case OLOCK_STATE_WAITING:
|
|
rem_node(&l->n);
|
|
break;
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
olock_dump(resource *r)
|
|
{
|
|
struct object_lock *l = (struct object_lock *) r;
|
|
static char *olock_states[] = { "free", "locked", "waiting", "event" };
|
|
|
|
debug("(%d:%s:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, l->inst, olock_states[l->state]);
|
|
if (!EMPTY_LIST(l->waiters))
|
|
debug(" [wanted]\n");
|
|
}
|
|
|
|
static struct resclass olock_class = {
|
|
"ObjLock",
|
|
sizeof(struct object_lock),
|
|
olock_free,
|
|
olock_dump,
|
|
NULL
|
|
};
|
|
|
|
/**
|
|
* olock_new - create an object lock
|
|
* @p: resource pool to create the lock in.
|
|
*
|
|
* The olock_new() function creates a new resource of type &object_lock
|
|
* and returns a pointer to it. After filling in the structure, the caller
|
|
* should call olock_acquire() to do the real locking.
|
|
*/
|
|
struct object_lock *
|
|
olock_new(pool *p)
|
|
{
|
|
struct object_lock *l = ralloc(p, &olock_class);
|
|
|
|
l->state = OLOCK_STATE_FREE;
|
|
init_list(&l->waiters);
|
|
return l;
|
|
}
|
|
|
|
/**
|
|
* olock_acquire - acquire a lock
|
|
* @l: the lock to acquire
|
|
*
|
|
* This function attempts to acquire exclusive access to the non-shareable
|
|
* resource described by the lock @l. It returns immediately, but as soon
|
|
* as the resource becomes available, it calls the hook() function set up
|
|
* by the caller.
|
|
*
|
|
* When you want to release the resource, just rfree() the lock.
|
|
*/
|
|
void
|
|
olock_acquire(struct object_lock *l)
|
|
{
|
|
node *n;
|
|
struct object_lock *q;
|
|
|
|
WALK_LIST(n, olock_list)
|
|
{
|
|
q = SKIP_BACK(struct object_lock, n, n);
|
|
if (olock_same(q, l))
|
|
{
|
|
l->state = OLOCK_STATE_WAITING;
|
|
add_tail(&q->waiters, &l->n);
|
|
DBG("olock: %p waits\n", l);
|
|
return;
|
|
}
|
|
}
|
|
DBG("olock: %p acquired immediately\n", l);
|
|
l->state = OLOCK_STATE_EVENT;
|
|
add_head(&olock_list, &l->n);
|
|
ev_schedule(olock_event);
|
|
}
|
|
|
|
static void
|
|
olock_run_event(void *unused UNUSED)
|
|
{
|
|
node *n;
|
|
struct object_lock *q;
|
|
|
|
DBG("olock: Processing events\n");
|
|
for(;;)
|
|
{
|
|
n = HEAD(olock_list);
|
|
if (!n->next)
|
|
break;
|
|
q = SKIP_BACK(struct object_lock, n, n);
|
|
if (q->state != OLOCK_STATE_EVENT)
|
|
break;
|
|
DBG("olock: %p locked\n", q);
|
|
q->state = OLOCK_STATE_LOCKED;
|
|
rem_node(&q->n);
|
|
add_tail(&olock_list, &q->n);
|
|
q->hook(q);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* olock_init - initialize the object lock mechanism
|
|
*
|
|
* This function is called during BIRD startup. It initializes
|
|
* all the internal data structures of the lock module.
|
|
*/
|
|
void
|
|
olock_init(void)
|
|
{
|
|
DBG("olock: init\n");
|
|
init_list(&olock_list);
|
|
olock_event = ev_new(&root_pool);
|
|
olock_event->hook = olock_run_event;
|
|
}
|