Add L2TP bridging offloading support
This commit is contained in:
parent
a957ff08ee
commit
38bfd3f738
2 changed files with 148 additions and 39 deletions
186
l2tpns.c
186
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue