Skip to content

Commit 6d5c365

Browse files
sthibaulkuba-moo
authored andcommitted
PPPoL2TP: Add more code snippets
The existing documentation was not telling that one has to create a PPP channel and a PPP interface to get PPPoL2TP data offloading working. Also, tunnel switching was not mentioned, so that people were thinking it was not supported, while it actually is. Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org> Acked-by: Tom Parkin <tparkin@katalix.com> Link: https://lore.kernel.org/r/20240217211425.qj576u3jmaa6yidf@begin Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent f796fea commit 6d5c365

File tree

1 file changed

+129
-6
lines changed

1 file changed

+129
-6
lines changed

Documentation/networking/l2tp.rst

Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -386,12 +386,19 @@ Sample userspace code:
386386

387387
- Create session PPPoX data socket::
388388

389+
/* Input: the L2TP tunnel UDP socket `tunnel_fd`, which needs to be
390+
* bound already (both sockname and peername), otherwise it will not be
391+
* ready.
392+
*/
393+
389394
struct sockaddr_pppol2tp sax;
390-
int fd;
395+
int session_fd;
396+
int ret;
397+
398+
session_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
399+
if (session_fd < 0)
400+
return -errno;
391401

392-
/* Note, the tunnel socket must be bound already, else it
393-
* will not be ready
394-
*/
395402
sax.sa_family = AF_PPPOX;
396403
sax.sa_protocol = PX_PROTO_OL2TP;
397404
sax.pppol2tp.fd = tunnel_fd;
@@ -406,12 +413,128 @@ Sample userspace code:
406413
/* session_fd is the fd of the session's PPPoL2TP socket.
407414
* tunnel_fd is the fd of the tunnel UDP / L2TPIP socket.
408415
*/
409-
fd = connect(session_fd, (struct sockaddr *)&sax, sizeof(sax));
410-
if (fd < 0 ) {
416+
ret = connect(session_fd, (struct sockaddr *)&sax, sizeof(sax));
417+
if (ret < 0 ) {
418+
close(session_fd);
419+
return -errno;
420+
}
421+
422+
return session_fd;
423+
424+
L2TP control packets will still be available for read on `tunnel_fd`.
425+
426+
- Create PPP channel::
427+
428+
/* Input: the session PPPoX data socket `session_fd` which was created
429+
* as described above.
430+
*/
431+
432+
int ppp_chan_fd;
433+
int chindx;
434+
int ret;
435+
436+
ret = ioctl(session_fd, PPPIOCGCHAN, &chindx);
437+
if (ret < 0)
438+
return -errno;
439+
440+
ppp_chan_fd = open("/dev/ppp", O_RDWR);
441+
if (ppp_chan_fd < 0)
442+
return -errno;
443+
444+
ret = ioctl(ppp_chan_fd, PPPIOCATTCHAN, &chindx);
445+
if (ret < 0) {
446+
close(ppp_chan_fd);
411447
return -errno;
412448
}
449+
450+
return ppp_chan_fd;
451+
452+
LCP PPP frames will be available for read on `ppp_chan_fd`.
453+
454+
- Create PPP interface::
455+
456+
/* Input: the PPP channel `ppp_chan_fd` which was created as described
457+
* above.
458+
*/
459+
460+
int ifunit = -1;
461+
int ppp_if_fd;
462+
int ret;
463+
464+
ppp_if_fd = open("/dev/ppp", O_RDWR);
465+
if (ppp_if_fd < 0)
466+
return -errno;
467+
468+
ret = ioctl(ppp_if_fd, PPPIOCNEWUNIT, &ifunit);
469+
if (ret < 0) {
470+
close(ppp_if_fd);
471+
return -errno;
472+
}
473+
474+
ret = ioctl(ppp_chan_fd, PPPIOCCONNECT, &ifunit);
475+
if (ret < 0) {
476+
close(ppp_if_fd);
477+
return -errno;
478+
}
479+
480+
return ppp_if_fd;
481+
482+
IPCP/IPv6CP PPP frames will be available for read on `ppp_if_fd`.
483+
484+
The ppp<ifunit> interface can then be configured as usual with netlink's
485+
RTM_NEWLINK, RTM_NEWADDR, RTM_NEWROUTE, or ioctl's SIOCSIFMTU, SIOCSIFADDR,
486+
SIOCSIFDSTADDR, SIOCSIFNETMASK, SIOCSIFFLAGS, or with the `ip` command.
487+
488+
- Bridging L2TP sessions which have PPP pseudowire types (this is also called
489+
L2TP tunnel switching or L2TP multihop) is supported by bridging the PPP
490+
channels of the two L2TP sessions to be bridged::
491+
492+
/* Input: the session PPPoX data sockets `session_fd1` and `session_fd2`
493+
* which were created as described further above.
494+
*/
495+
496+
int ppp_chan_fd;
497+
int chindx1;
498+
int chindx2;
499+
int ret;
500+
501+
ret = ioctl(session_fd1, PPPIOCGCHAN, &chindx1);
502+
if (ret < 0)
503+
return -errno;
504+
505+
ret = ioctl(session_fd2, PPPIOCGCHAN, &chindx2);
506+
if (ret < 0)
507+
return -errno;
508+
509+
ppp_chan_fd = open("/dev/ppp", O_RDWR);
510+
if (ppp_chan_fd < 0)
511+
return -errno;
512+
513+
ret = ioctl(ppp_chan_fd, PPPIOCATTCHAN, &chindx1);
514+
if (ret < 0) {
515+
close(ppp_chan_fd);
516+
return -errno;
517+
}
518+
519+
ret = ioctl(ppp_chan_fd, PPPIOCBRIDGECHAN, &chindx2);
520+
close(ppp_chan_fd);
521+
if (ret < 0)
522+
return -errno;
523+
413524
return 0;
414525

526+
It can be noted that when bridging PPP channels, the PPP session is not locally
527+
terminated, and no local PPP interface is created. PPP frames arriving on one
528+
channel are directly passed to the other channel, and vice versa.
529+
530+
The PPP channel does not need to be kept open. Only the session PPPoX data
531+
sockets need to be kept open.
532+
533+
More generally, it is also possible in the same way to e.g. bridge a PPPoL2TP
534+
PPP channel with other types of PPP channels, such as PPPoE.
535+
536+
See more details for the PPP side in ppp_generic.rst.
537+
415538
Old L2TPv2-only API
416539
-------------------
417540

0 commit comments

Comments
 (0)