From b2bc6da8275b28368792ef56f4e477d9140027dd Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 23 Apr 2023 23:01:05 +0200 Subject: [PATCH] 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. --- l2tpns.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 192 insertions(+), 4 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 658b87c..3e7c9d6 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -471,6 +471,132 @@ void random_data(uint8_t *buf, int len) 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 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 -static int delete_kernel_tunnel(uint32_t tid) +// Queue deleting tunnel in kernel +static int queue_delete_kernel_tunnel(uint32_t tid) { struct { struct nlmsghdr nh; @@ -565,6 +691,23 @@ static int delete_kernel_tunnel(uint32_t tid) 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)); if (size < 0) { @@ -577,6 +720,20 @@ static int delete_kernel_tunnel(uint32_t tid) 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 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 -static int delete_kernel_session(uint32_t tid, uint32_t sid) +// Queue deleting session in kernel +static int queue_delete_kernel_session(uint32_t tid, uint32_t sid) { struct { struct nlmsghdr nh; @@ -669,6 +826,23 @@ static int delete_kernel_session(uint32_t tid, uint32_t sid) 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)); if (size < 0) { @@ -681,6 +855,20 @@ static int delete_kernel_session(uint32_t tid, uint32_t sid) 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 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)