Add support for deleting all kernel tunnels/sessions

Unfortunately, tunnels and session can survive us, so we have to drop any
tunnel/session left from a previous instance that might have crashed.
This commit is contained in:
Samuel Thibault 2023-04-23 23:01:05 +02:00
parent 1f4d79ce85
commit b2bc6da827

196
l2tpns.c
View file

@ -471,6 +471,132 @@ void random_data(uint8_t *buf, int len)
buf[n++] = (rand() >> 4) & 0xff; buf[n++] = (rand() >> 4) & 0xff;
} }
//
// Clear all existing kernel items of a given type
static int delete_kernel_items(const char *name, int cmd, int id1, int id2, void (*delete_one)(uint32_t id1, uint32_t id2))
{
struct {
struct nlmsghdr nh;
struct genlmsghdr glh;
char data[8192];
} req;
int seqnum;
if (genl_l2tp_id < 0)
{
errno = ENOSYS;
return -1;
}
LOG(3, 0, 0, "Deleting all kernel %ss\n", name);
memset(&req, 0, sizeof(req));
req.nh.nlmsg_type = genl_l2tp_id;
req.nh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_DUMP;
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.glh));
req.glh.cmd = cmd;
req.glh.version = L2TP_GENL_VERSION;
assert(req.nh.nlmsg_len < sizeof(req));
if (genetlink_send(&req.nh) < 0)
{
LOG(2, 0, 0, "Can't delete %ss: %s\n", name, strerror(errno));
return -1;
}
seqnum = genlseqnum;
/* 1 for receiving "done" */
int nitems = 1;
int done = 0;
while (done < nitems)
{
ssize_t size = genetlink_recv(&req, sizeof(req));
if (size < 0)
{
LOG(2, 0, 0, "Can't receive answer for %s deletion: %s\n", name, strerror(errno));
return -1;
}
// Iterate over all answers
struct nlmsghdr *nh;
for (nh = &req.nh; size; nh = NLMSG_NEXT(nh, size))
{
if (!NLMSG_OK(nh, size))
{
LOG(2, 0, 0, "Short netlink answer: %d vs %zd\n", nh->nlmsg_len, size);
break;
}
if (nh->nlmsg_type == NLMSG_NOOP)
{
// Ignore
continue;
}
if (nh->nlmsg_type == NLMSG_DONE)
{
done++;
if (done < nitems)
LOG(3, 0, 0, "Done queueing, still %d/%d %ss deletion pending\n", done, nitems, name);
continue;
}
if (nh->nlmsg_seq != seqnum)
{
// Consume acknoledgments of deletions.
netlink_handle_ack(nh, 1, 0, NULL);
done++;
}
else
{
// Getting more items
if (nh->nlmsg_type != genl_l2tp_id)
{
LOG(2, 0, 0, "Unexpected generic netlink answer %d\n", req.nh.nlmsg_type);
continue;
}
if (nh->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN)
{
LOG(2, 0, 0, "Short answer for l2tp netlink name\n");
continue;
}
uint32_t ret;
if (genetlink_getattr(nh, id1, &ret, sizeof(ret)) != 0)
LOG(2, 0, 0, "Did not get %s ID\n", name);
else
{
if (!id2)
{
delete_one(ret, 0);
nitems++;
}
else
{
uint32_t ret2;
if (genetlink_getattr(nh, id2, &ret2, sizeof(ret2)) != 0)
LOG(2, 0, 0, "Did not get %s ID2\n", name);
else
{
// Queue deletion for this
delete_one(ret, ret2);
nitems++;
}
}
}
}
}
}
LOG(3, 0, 0, "Done deleting %ss\n", name);
return 0;
}
// //
// Create tunnel in kernel // Create tunnel in kernel
static int create_kernel_tunnel(uint32_t tid, uint32_t peer_tid) static int create_kernel_tunnel(uint32_t tid, uint32_t peer_tid)
@ -529,8 +655,8 @@ static int create_kernel_tunnel(uint32_t tid, uint32_t peer_tid)
} }
// //
// Delete tunnel in kernel // Queue deleting tunnel in kernel
static int delete_kernel_tunnel(uint32_t tid) static int queue_delete_kernel_tunnel(uint32_t tid)
{ {
struct { struct {
struct nlmsghdr nh; struct nlmsghdr nh;
@ -565,6 +691,23 @@ static int delete_kernel_tunnel(uint32_t tid)
return -1; return -1;
} }
return 0;
}
//
// Delete tunnel in kernel
static int delete_kernel_tunnel(uint32_t tid)
{
int ret = queue_delete_kernel_tunnel(tid);
if (ret < 0)
return -1;
struct {
struct nlmsghdr nh;
struct genlmsghdr glh;
char data[64];
} req;
ssize_t size = genetlink_recv(&req, sizeof(req)); ssize_t size = genetlink_recv(&req, sizeof(req));
if (size < 0) if (size < 0)
{ {
@ -577,6 +720,20 @@ static int delete_kernel_tunnel(uint32_t tid)
return 0; return 0;
} }
//
// Clear all existing tunnels
//
// Unfortunately, tunnels survive us, so we have to drop any tunnel left from a
// previous instance that might have crashed.
static void delete_one_kernel_tunnel(uint32_t id1, uint32_t id2)
{
queue_delete_kernel_tunnel(id1);
}
static void delete_kernel_tunnels(void)
{
delete_kernel_items("tunnel", L2TP_CMD_TUNNEL_GET, L2TP_ATTR_CONN_ID, L2TP_ATTR_NONE, delete_one_kernel_tunnel);
}
// //
// Create session in kernel // Create session in kernel
static int create_kernel_session(uint32_t tid, uint32_t peer_tid, uint32_t sid, uint32_t peer_sid) static int create_kernel_session(uint32_t tid, uint32_t peer_tid, uint32_t sid, uint32_t peer_sid)
@ -632,8 +789,8 @@ static int create_kernel_session(uint32_t tid, uint32_t peer_tid, uint32_t sid,
} }
// //
// Delete session in kernel // Queue deleting session in kernel
static int delete_kernel_session(uint32_t tid, uint32_t sid) static int queue_delete_kernel_session(uint32_t tid, uint32_t sid)
{ {
struct { struct {
struct nlmsghdr nh; struct nlmsghdr nh;
@ -669,6 +826,23 @@ static int delete_kernel_session(uint32_t tid, uint32_t sid)
return -1; return -1;
} }
return 0;
}
//
// Delete session in kernel
static int delete_kernel_session(uint32_t tid, uint32_t sid)
{
int ret = queue_delete_kernel_session(tid, sid);
if (ret < 0)
return -1;
struct {
struct nlmsghdr nh;
struct genlmsghdr glh;
char data[64];
} req;
ssize_t size = genetlink_recv(&req, sizeof(req)); ssize_t size = genetlink_recv(&req, sizeof(req));
if (size < 0) if (size < 0)
{ {
@ -681,6 +855,20 @@ static int delete_kernel_session(uint32_t tid, uint32_t sid)
return 0; return 0;
} }
//
// Clear all existing sessions
//
// Unfortunately, sessions survive us, so we have to drop any session left from a
// previous instance that might have crashed.
static void delete_one_kernel_session(uint32_t id1, uint32_t id2)
{
queue_delete_kernel_session(id2, id1);
}
static void delete_kernel_sessions(void)
{
delete_kernel_items("session", L2TP_CMD_SESSION_GET, L2TP_ATTR_SESSION_ID, L2TP_ATTR_CONN_ID, delete_one_kernel_session);
}
// //
// Create the kernel PPPoX socket // Create the kernel PPPoX socket
static int create_ppp_socket(int udp_fd, uint32_t tid, uint32_t peer_tid, uint32_t sid, uint32_t peer_sid, const struct sockaddr *dst, socklen_t addrlen) static int create_ppp_socket(int udp_fd, uint32_t tid, uint32_t peer_tid, uint32_t sid, uint32_t peer_sid, const struct sockaddr *dst, socklen_t addrlen)