Add L2TP bridging offloading support

This commit is contained in:
Samuel Thibault 2024-02-03 18:34:43 +01:00
parent a957ff08ee
commit 38bfd3f738
2 changed files with 148 additions and 39 deletions

186
l2tpns.c
View file

@ -652,9 +652,19 @@ static int create_kernel_tunnel(uint32_t tid, uint32_t peer_tid)
req.glh.version = L2TP_GENL_VERSION; req.glh.version = L2TP_GENL_VERSION;
int fd; int fd;
if (initudp(&fd, config->bind_n_address[tunnel[tid].indexudp], if (tunnel[tid].indexudp == config->indexlacudpfd)
htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0) {
return -1; /* 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_FD, &fd, sizeof(fd));
genetlink_addattr(&req.nh, L2TP_ATTR_CONN_ID, &tid, sizeof(tid)); 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 */ /* MPPP not supported yet */
return 0; return 0;
if (session[s].forwardtosession)
/* Forwarding not supported yet */
return 0;
if (session[s].throttle_in || session[s].throttle_out) if (session[s].throttle_in || session[s].throttle_out)
/* Throttling not supported */ /* Throttling not supported */
return 0; return 0;
@ -1247,11 +1253,92 @@ err_pppox_fd:
return -1; 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 // Delete the kernel PPP acceleration
static int delete_kernel_accel(sessionidt s) static int delete_kernel_accel(sessionidt s)
{ {
if (!sess_local[s].ppp_if_idx) if (sess_local[s].pppox_fd < 0)
/* Already stopped */ /* Already stopped */
return 0; 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_unit = -1;
sess_local[s].ppp_if_idx = 0; 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); if (sess_local[s].ppp_if_fd >= 0)
close(sess_local[s].ppp_if_fd); {
sess_local[s].ppp_if_fd = -1; 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); if (sess_local[s].ppp_chan_fd >= 0)
close(sess_local[s].ppp_chan_fd); {
sess_local[s].ppp_chan_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;
}
epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].pppox_fd, NULL); epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].pppox_fd, NULL);
close(sess_local[s].pppox_fd); close(sess_local[s].pppox_fd);
@ -1290,9 +1384,26 @@ static void set_kernel_accel(sessionidt s, int set, int now)
return; return;
tunnelidt t = session[s].tunnel; tunnelidt t = session[s].tunnel;
if (set && tunnel[t].state == TUNNELUNDEF) sessionidt fwds = session[s].forwardtosession;
/* We don't know the tunnel yet */
return; 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) 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) if (set)
{ {
create_kernel_tunnel(t, tunnel[t].far); 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 else
{
delete_kernel_accel(s); delete_kernel_accel(s);
if (fwds)
delete_kernel_accel(fwds);
}
routesset(s, &session[s], 1); routesset(s, &session[s], 1);
if (session[s].ppp.ipv6cp == Opened) 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 // acceleration is allowed, e.g. snoop
void switch_kernel_accel(sessionidt s) void switch_kernel_accel(sessionidt s)
{ {
if (!sess_local[s].ppp_if_idx) if (sess_local[s].pppox_fd < 0)
{ {
/* Acceleration disabled */ /* Acceleration disabled */
@ -1361,7 +1484,7 @@ static void apply_kernel_stats(sessionidt s)
/* It is free */ /* It is free */
return; return;
if (!sess_local[s].ppp_if_idx) if (sess_local[s].pppox_fd < 0)
/* It does not have kernel acceleration */ /* It does not have kernel acceleration */
return; return;
@ -1391,24 +1514,6 @@ static void apply_kernel_stats(sessionidt s)
*last_stats = stats; *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 // Get interface idx for session
static int session_if_idx(sessionidt s) 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].far = asession;
session[s].last_packet = session[s].last_data = time_now; 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, 19, 1, 1); // Framing Type
control32(c, 24, 10000000, 1); // Tx Connect Speed control32(c, 24, 10000000, 1); // Tx Connect Speed
controladd(c, asession, t); // send the message controladd(c, asession, t); // send the message

View file

@ -1039,6 +1039,7 @@ void rebuild_address_pool(void);
void throttle_session(sessionidt s, int rate_in, int rate_out); void throttle_session(sessionidt s, int rate_in, int rate_out);
int load_tunnel(tunnelidt, tunnelt *); int load_tunnel(tunnelidt, tunnelt *);
int load_session(sessionidt, sessiont *); 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 become_master(void); // We're the master; kick off any required master initializations.
void crash(void); // We messed up. Die. void crash(void); // We messed up. Die.