From 38bfd3f738cc4967779d67d940f57f89eddf43db Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sat, 3 Feb 2024 18:34:43 +0100 Subject: [PATCH] Add L2TP bridging offloading support --- l2tpns.c | 186 +++++++++++++++++++++++++++++++++++++++++++------------ l2tpns.h | 1 + 2 files changed, 148 insertions(+), 39 deletions(-) diff --git a/l2tpns.c b/l2tpns.c index 04ad4e0..fa7cc2d 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -652,9 +652,19 @@ static int create_kernel_tunnel(uint32_t tid, uint32_t peer_tid) req.glh.version = L2TP_GENL_VERSION; int fd; - if (initudp(&fd, config->bind_n_address[tunnel[tid].indexudp], - htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0) - return -1; + if (tunnel[tid].indexudp == config->indexlacudpfd) + { + /* tunnel as LAC */ + if (initlacudp(&fd, htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0) + return -1; + } + else + { + /* tunnel as LNS */ + if (initudp(&fd, config->bind_n_address[tunnel[tid].indexudp], + htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0) + return -1; + } genetlink_addattr(&req.nh, L2TP_ATTR_FD, &fd, sizeof(fd)); genetlink_addattr(&req.nh, L2TP_ATTR_CONN_ID, &tid, sizeof(tid)); @@ -1136,10 +1146,6 @@ static int can_kernel_accel(sessionidt s) /* MPPP not supported yet */ return 0; - if (session[s].forwardtosession) - /* Forwarding not supported yet */ - return 0; - if (session[s].throttle_in || session[s].throttle_out) /* Throttling not supported */ return 0; @@ -1247,11 +1253,92 @@ err_pppox_fd: return -1; } +// +// Create the kernel PPP accelerated bridge +int create_kernel_bridge(sessionidt s, sessionidt fwds) +{ + static int kernel_cant = 0; + + tunnelidt t = session[s].tunnel; + + if (fwds == s) + /* Meaningless! */ + return -1; + + if (kernel_cant) + /* We have seen that kernel can't do it anyway */ + return -1; + + if (sess_local[s].pppox_fd >= 0) + /* Already set up */ + return 0; + + if (!can_kernel_accel(s) || !can_kernel_accel(fwds)) + return -1; + + int pppox_fd = create_kernel_pppox(s); + if (pppox_fd < 0) + return -1; + + int fwd_pppox_fd = create_kernel_pppox(fwds); + if (fwd_pppox_fd < 0) + goto err_pppox_fd; + + LOG(3, s, t, "Starting kernel-accelerated bridge between %u and %u\n", s, fwds); + + int ppp_chan_fd = create_kernel_ppp_chan(s, pppox_fd); + if (ppp_chan_fd < 0) + goto err_fwd_pppox_fd; + + int fwd_idx = get_kernel_ppp_chan(fwds, fwd_pppox_fd); + + int ret = ioctl(ppp_chan_fd, PPPIOCBRIDGECHAN, &fwd_idx); + close(ppp_chan_fd); + if (ret < 0) { + if (errno == ENOTTY) + /* Not supported by kernel */ + kernel_cant = 1; + + LOG(2, s, session[s].tunnel, "Can't set LAC bridge: %s\n", strerror(errno)); + goto err_fwd_pppox_fd; + } + + struct epoll_event e; + e.events = EPOLLIN; + + static struct event_data d1[MAXSESSION]; + d1[s].type = FD_TYPE_PPPOX; + d1[s].index = s; + e.data.ptr = &d1[s]; + + epoll_ctl(epollfd, EPOLL_CTL_ADD, pppox_fd, &e); + + d1[fwds].type = FD_TYPE_PPPOX; + d1[fwds].index = fwds; + e.data.ptr = &d1[fwds]; + + epoll_ctl(epollfd, EPOLL_CTL_ADD, fwd_pppox_fd, &e); + + sess_local[s].pppox_fd = pppox_fd; + sess_local[fwds].pppox_fd = fwd_pppox_fd; + + memset(&sess_local[s].last_stats, 0, sizeof(sess_local[s].last_stats)); + memset(&sess_local[fwds].last_stats, 0, sizeof(sess_local[fwds].last_stats)); + + return 0; + +err_fwd_pppox_fd: + close(fwd_pppox_fd); +err_pppox_fd: + close(pppox_fd); + return -1; +} + // // Delete the kernel PPP acceleration static int delete_kernel_accel(sessionidt s) { - if (!sess_local[s].ppp_if_idx) + if (sess_local[s].pppox_fd < 0) /* Already stopped */ return 0; @@ -1260,15 +1347,22 @@ static int delete_kernel_accel(sessionidt s) sess_local[s].ppp_if_unit = -1; sess_local[s].ppp_if_idx = 0; - ioctl(sess_local[s].ppp_chan_fd, PPPIOCDISCONN); + if (sess_local[s].ppp_chan_fd >= 0) + ioctl(sess_local[s].ppp_chan_fd, PPPIOCDISCONN); - epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_if_fd, NULL); - close(sess_local[s].ppp_if_fd); - sess_local[s].ppp_if_fd = -1; + if (sess_local[s].ppp_if_fd >= 0) + { + epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_if_fd, NULL); + close(sess_local[s].ppp_if_fd); + sess_local[s].ppp_if_fd = -1; + } - epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_chan_fd, NULL); - close(sess_local[s].ppp_chan_fd); - sess_local[s].ppp_chan_fd = -1; + if (sess_local[s].ppp_chan_fd >= 0) + { + epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_chan_fd, NULL); + close(sess_local[s].ppp_chan_fd); + sess_local[s].ppp_chan_fd = -1; + } epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].pppox_fd, NULL); close(sess_local[s].pppox_fd); @@ -1290,9 +1384,26 @@ static void set_kernel_accel(sessionidt s, int set, int now) return; tunnelidt t = session[s].tunnel; - if (set && tunnel[t].state == TUNNELUNDEF) - /* We don't know the tunnel yet */ - return; + sessionidt fwds = session[s].forwardtosession; + + if (set) + { + if (tunnel[t].state == TUNNELUNDEF) + /* We don't know the tunnel yet */ + return; + + if (fwds) + { + if (session[fwds].forwardtosession != s) + /* We don't know the other session yet */ + return; + + tunnelidt fwdt = session[fwds].tunnel; + if (tunnel[fwdt].state == TUNNELUNDEF) + /* We don't know the tunnel yet */ + return; + } + } if (set && !now && kernel_switches >= MAX_KERNEL_SWITCHES) { @@ -1311,10 +1422,22 @@ static void set_kernel_accel(sessionidt s, int set, int now) if (set) { create_kernel_tunnel(t, tunnel[t].far); - create_kernel_accel(s); + + if (fwds) + { + tunnelidt fwdt = session[fwds].tunnel; + create_kernel_tunnel(fwdt, tunnel[fwdt].far); + create_kernel_bridge(s, fwds); + } + else + create_kernel_accel(s); } else + { delete_kernel_accel(s); + if (fwds) + delete_kernel_accel(fwds); + } routesset(s, &session[s], 1); if (session[s].ppp.ipv6cp == Opened) @@ -1327,7 +1450,7 @@ static void set_kernel_accel(sessionidt s, int set, int now) // acceleration is allowed, e.g. snoop void switch_kernel_accel(sessionidt s) { - if (!sess_local[s].ppp_if_idx) + if (sess_local[s].pppox_fd < 0) { /* Acceleration disabled */ @@ -1361,7 +1484,7 @@ static void apply_kernel_stats(sessionidt s) /* It is free */ return; - if (!sess_local[s].ppp_if_idx) + if (sess_local[s].pppox_fd < 0) /* It does not have kernel acceleration */ return; @@ -1391,24 +1514,6 @@ static void apply_kernel_stats(sessionidt s) *last_stats = stats; } -// -// Bridge kernel channels to accelerate LAC -static int bridge_kernel_chans(sessionidt s, int pppox_fd, int pppox_fd2) -{ - int ppp_chan_fd = create_kernel_ppp_chan(s, pppox_fd); - int chindx2 = get_kernel_ppp_chan(s, pppox_fd2); - int ret; - - ret = ioctl(ppp_chan_fd, PPPIOCBRIDGECHAN, &chindx2); - close(ppp_chan_fd); - if (ret < 0) - { - LOG(2, s, session[s].tunnel, "Can't set LAC bridge: %s\n", strerror(errno)); - return -1; - } - return 0; -} - // Get interface idx for session static int session_if_idx(sessionidt s) { @@ -4722,6 +4827,9 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr, uint16_t indexu session[s].far = asession; session[s].last_packet = session[s].last_data = time_now; + // Now we have the far session number, we can try to enable accelerated forward + create_kernel_bridge(s, session[s].forwardtosession); + control32(c, 19, 1, 1); // Framing Type control32(c, 24, 10000000, 1); // Tx Connect Speed controladd(c, asession, t); // send the message diff --git a/l2tpns.h b/l2tpns.h index 59852b9..74c6a02 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -1039,6 +1039,7 @@ void rebuild_address_pool(void); void throttle_session(sessionidt s, int rate_in, int rate_out); int load_tunnel(tunnelidt, tunnelt *); int load_session(sessionidt, sessiont *); +int create_kernel_bridge(sessionidt s, sessionidt fwds); void become_master(void); // We're the master; kick off any required master initializations. void crash(void); // We messed up. Die.