Add L2TP bridging offloading support
This commit is contained in:
parent
a957ff08ee
commit
38bfd3f738
2 changed files with 148 additions and 39 deletions
160
l2tpns.c
160
l2tpns.c
|
|
@ -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 (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],
|
if (initudp(&fd, config->bind_n_address[tunnel[tid].indexudp],
|
||||||
htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0)
|
htonl(tunnel[tid].ip), htons(tunnel[tid].port)) < 0)
|
||||||
return -1;
|
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;
|
||||||
|
|
||||||
|
if (sess_local[s].ppp_chan_fd >= 0)
|
||||||
ioctl(sess_local[s].ppp_chan_fd, PPPIOCDISCONN);
|
ioctl(sess_local[s].ppp_chan_fd, PPPIOCDISCONN);
|
||||||
|
|
||||||
|
if (sess_local[s].ppp_if_fd >= 0)
|
||||||
|
{
|
||||||
epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_if_fd, NULL);
|
epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_if_fd, NULL);
|
||||||
close(sess_local[s].ppp_if_fd);
|
close(sess_local[s].ppp_if_fd);
|
||||||
sess_local[s].ppp_if_fd = -1;
|
sess_local[s].ppp_if_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sess_local[s].ppp_chan_fd >= 0)
|
||||||
|
{
|
||||||
epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_chan_fd, NULL);
|
epoll_ctl(epollfd, EPOLL_CTL_DEL, sess_local[s].ppp_chan_fd, NULL);
|
||||||
close(sess_local[s].ppp_chan_fd);
|
close(sess_local[s].ppp_chan_fd);
|
||||||
sess_local[s].ppp_chan_fd = -1;
|
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,10 +1384,27 @@ 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;
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
{
|
||||||
|
if (tunnel[t].state == TUNNELUNDEF)
|
||||||
/* We don't know the tunnel yet */
|
/* We don't know the tunnel yet */
|
||||||
return;
|
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)
|
||||||
{
|
{
|
||||||
// We already performed many switches, throttle a bit by just
|
// We already performed many switches, throttle a bit by just
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
if (fwds)
|
||||||
|
{
|
||||||
|
tunnelidt fwdt = session[fwds].tunnel;
|
||||||
|
create_kernel_tunnel(fwdt, tunnel[fwdt].far);
|
||||||
|
create_kernel_bridge(s, fwds);
|
||||||
|
}
|
||||||
|
else
|
||||||
create_kernel_accel(s);
|
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
|
||||||
|
|
|
||||||
1
l2tpns.h
1
l2tpns.h
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue