Expand description
§l2tp-rs
High-level async control API for the Linux kernel’s L2TP subsystem, with a blocking counterpart, over Generic Netlink.
This crate covers the tunnel and session lifecycle, lookups, statistics and
notifications of the kernel’s l2tp family, the managed tunnel sockets behind
tunnels, and the PPPoL2TP sockets behind PPP pseudowires. It does not
configure interfaces, bridges or routes; rtnetlink tasks belong in a
separate crate.
§Requirements
- Linux. On other platforms the crate compiles but exposes no API, and the
l2tpbinary only reports that the platform is unsupported. - Rust 1.89 or newer (required by the netlink dependencies).
- Linux kernel with L2TP Generic Netlink support (
l2tpfamily). Ethernet pseudowires needl2tp_eth, PPP pseudowiresl2tp_ppp, and IP encapsulationl2tp_iporl2tp_ip6. - Tokio runtime with its I/O driver enabled, or the
blockingmodule, which brings its own. CAP_NET_ADMIN(or root) for every L2TP netlink command, includinggetandlist; the kernel marks them allGENL_UNS_ADMIN_PERM. Subscribing to notifications needs no privilege.
§Installation
[dependencies]
l2tp = "0.4"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }§Cargo features
serde:Serialize/Deserializefor the configuration, information, event, identifier and statistics types. Endpoints are strings such as192.0.2.1:1701orfe80::1%eth0, cookies are hexadecimal strings, and interface names are strings, or byte arrays when they are not UTF-8.cli: builds the supplementary, testing-onlyl2tpcommand-line tool (impliesserde).
No features are enabled by default, so library users do not pull in the CLI’s dependencies.
§Command-Line Tool
The l2tp CLI is a supplementary tool intended only for testing the library.
Its text and JSON output are for inspection; serialization losses, such as
omitted IPv6 UDP flow information, are expected. Use the Rust API for
applications that need complete configuration values.
The l2tp binary is behind the cli feature. Install it with:
cargo install --features cli l2tpFrom a checkout, use cargo run --features cli -- <args>. Besides tunnel and
session management, l2tp monitor prints kernel notifications as they
happen, and sessions can be selected by --ifname instead of their
identifiers.
For UDP tunnels, omitting --local binds the IPv6 wildcard ::; an IPv4
remote is then mapped to IPv6. Pass --local 0.0.0.0 (or an IPv4 address,
optionally with a port) to create an IPv4 socket. An explicit IPv4 --local
is required when using an IPv4 remote with --no-udp-csum.
§Quick Start
§Managed UDP tunnel + Ethernet session
use l2tp::{
Encapsulation, L2tpHandle, SessionConfig, SessionId, TunnelConfig, TunnelId,
TunnelSocket, UdpEndpoint,
};
use std::net::{Ipv4Addr, SocketAddrV4};
#[tokio::main]
async fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new().await?;
let local = UdpEndpoint::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 1), 1701));
let remote = UdpEndpoint::V4(SocketAddrV4::new(Ipv4Addr::new(10, 0, 0, 2), 1701));
let tunnel_cfg = TunnelConfig::new(
TunnelId(1000),
TunnelId(2000),
Encapsulation::Udp {
local,
remote,
udp_csum: false,
udp_zero_csum6_tx: false,
udp_zero_csum6_rx: false,
},
)?;
// Managed socket: fd is passed via L2TP_ATTR_FD and owned by TunnelHandle.
let socket = TunnelSocket::for_config(&tunnel_cfg, None)?;
let tunnel = handle.create_tunnel(tunnel_cfg, socket).await?;
let mut session_cfg =
SessionConfig::eth(tunnel.tunnel_id(), SessionId(3000), SessionId(4000));
// A unique name of your own; the kernel keeps it for the session's life.
session_cfg.ifname = Some("l2tp-3000".parse()?);
let session = handle.create_session(session_cfg).await?;
// Address the device by index from here on; udev may rename it any time.
let _ifindex = session.ifindex();
let _tunnel_info = tunnel.get().await?;
let _session_info = session.get().await?;
Ok(())
}§Managed IP-backed tunnel (IPv4 or IPv6)
use l2tp::{Encapsulation, IpEndpoint, L2tpHandle, TunnelConfig, TunnelId, TunnelSocket};
use std::net::Ipv4Addr;
#[tokio::main]
async fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new().await?;
let local = IpEndpoint::V4(Ipv4Addr::new(192, 0, 2, 1));
let remote = IpEndpoint::V4(Ipv4Addr::new(192, 0, 2, 2));
let socket = TunnelSocket::ip(&local, &remote, None, TunnelId(5000), TunnelId(6000))?;
let cfg = TunnelConfig::new(
TunnelId(5000),
TunnelId(6000),
Encapsulation::Ip { local, remote },
)?;
let _tunnel = handle.create_tunnel(cfg, socket).await?;
Ok(())
}§Link-local IPv6 endpoints
The kernel refuses to bind or connect a link-local address without an
interface. Scope the endpoint with an interface name; it is resolved to the
interface index each time the socket binds, connects or reconnects.
If the interface is re-created under a new index, call disconnect() before
reconnect_ip() to clear the kernel’s old interface binding. Reapply
bind_to_device() first if that binding is needed, since disconnect clears it.
The scopes of both endpoints and the device must name the same interface.
use l2tp::{
Encapsulation, IpEndpoint, L2tpHandle, L2tpIfName, TunnelConfig, TunnelId,
TunnelSocket,
};
use std::net::Ipv6Addr;
#[tokio::main]
async fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new().await?;
let eth0: L2tpIfName = "eth0".parse()?;
let local =
IpEndpoint::v6_scoped(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), eth0);
// A link-local peer without a scope uses the local endpoint's interface.
let remote = IpEndpoint::v6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 2));
let cfg = TunnelConfig::new(
TunnelId(7000),
TunnelId(8000),
Encapsulation::Ip { local, remote },
)?;
let socket = TunnelSocket::for_config(&cfg, None)?;
let _tunnel = handle.create_tunnel(cfg, socket).await?;
Ok(())
}UDP endpoints carry the numeric scope ID of SocketAddrV6. Resolve the name
with L2tpIfNameExt::index right before creating or reconnecting the socket:
use l2tp::L2tpIfNameExt;
let scope_id = L2tpIfName::new("eth0")?.index()?;
let local = UdpEndpoint::V6(SocketAddrV6::new(link_local, 1701, 0, scope_id));A link-local remote may omit the scope ID once the local endpoint, the
device, or an earlier scoped connect has pinned the socket to an interface.
Both socket types report a missing scope as Error::ScopeRequired before
the socket is created.
Unmanaged tunnels cannot use link-local endpoints, because the kernel creates
their socket without an interface scope; create_unmanaged_tunnel returns
Error::UnmanagedLinkLocal before contacting the kernel. Re-creating a tunnel
after its interface disappears is left to the application. L2TP notifications
do not report interface removal; detect link changes separately.
§Protocol Versions
TunnelConfig::new creates L2TPv3 tunnels. with_version(L2tpVersion::V2)
creates an L2TPv2 tunnel, whose sessions are PPP pseudowires with 16-bit
identifiers and UDP encapsulation. V2 with IP encapsulation is rejected with
Error::V2IpEncapsulation: the kernel accepts creation but its IP receive
path cannot find V2 sessions. The kernel ignores cookies and the L2-specific
sublayer for V2 sessions. Ethernet pseudowires need version 3.
TunnelInfo::version reports the version of an existing tunnel.
§PPP Pseudowires
A PPP session created through netlink becomes usable once a PPPoL2TP
socket is connected to it. PppSocket::connect attaches to the session and
yields the descriptor that carries PPP frames; PPP negotiation stays in user
space, for example in pppd with its pppol2tp <fd> option. PppUnit
creates a pppN interface and PppUnit::connect_channel connects the
channel to it for applications that implement PPP themselves; dropping the
returned PppChannel disconnects the channel again.
The unit and socket retain their creating network namespaces, including
container namespaces. Connecting objects from different namespaces returns
Error::NamespaceMismatch. Attaching from another thread namespace runs in
the owning namespace and requires permission to enter it; ordinary attachment
within the same namespace does not need setns privileges.
The kernel deletes the session when the last descriptor of its PPPoL2TP
socket is closed, without a netlink event, so the socket’s lifetime is the
session’s lifetime. Disable auto-delete on the session handle, or its
deletion reports ENODEV, the kernel’s error for a missing tunnel or
session.
use l2tp::{L2tpHandle, L2tpVersion, PppSocket, SessionConfig, SessionId, TunnelId};
#[tokio::main]
async fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new().await?;
// The tunnel was created earlier, managed or unmanaged.
let tunnel = handle.open_tunnel(TunnelId(1000)).await?;
let session = handle
.create_session(SessionConfig::ppp(tunnel.tunnel_id(), SessionId(1), SessionId(2)))
.await?;
let ppp = PppSocket::connect(
L2tpVersion::V3,
tunnel.tunnel_id(),
TunnelId(2000),
session.session_id(),
SessionId(2),
)?;
let _channel = ppp.channel_index()?;
Ok(())
}§Events
The kernel announces tunnel and session creation, modification and deletion
through netlink commands on the l2tp multicast group. It announces nothing
for teardown it performs itself: a tunnel whose managed socket was closed,
the sessions removed with a deleted tunnel, or a PPP session whose
PPPoL2TP socket was released. Reconcile with list_tunnels and
list_all_sessions to catch those. L2tpHandle::events returns a Stream
of L2tpEvent; the blocking handle returns an iterator.
Some kernels, including Linux 6.8, broadcast these events into every network
namespace, including containers, without an origin-namespace identifier.
The crate cannot reliably distinguish those events: tunnel and session IDs
can be reused in different namespaces. Treat notifications as hints to
reconcile through list_tunnels, list_all_sessions, or other queries on
your handle. Do not apply a notification as a mutation of a local object.
This limitation also applies to plain L2tpHandle::new() inside a container;
no explicit namespace argument or netns ID is needed to encounter it.
use futures::StreamExt;
use l2tp::L2tpHandle;
#[tokio::main]
async fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new().await?;
let mut events = handle.events().await?;
while let Some(event) = events.next().await {
event?;
// Query this handle's namespace, regardless of the event's origin.
let tunnels = handle.list_tunnels().await?;
let sessions = handle.list_all_sessions().await?;
println!("local tunnels: {tunnels:?}; local sessions: {sessions:?}");
}
Ok(())
}§Blocking API
l2tp::blocking::L2tpHandle mirrors the async handle with blocking methods.
It runs a dedicated thread with its own Tokio runtime, so it works from plain
threads, from tokio::task::spawn_blocking, and from code that already runs a
runtime of its own. Its handles hand their async counterparts back to the
worker thread on drop, so auto-delete keeps working.
use l2tp::{TunnelId, blocking::L2tpHandle};
fn main() -> l2tp::Result<()> {
let handle = L2tpHandle::new()?;
for tunnel in handle.list_tunnels()? {
println!("{tunnel:?}");
}
let _stats = handle.tunnel_stats(TunnelId(1000))?;
Ok(())
}§Network Namespaces
Sockets belong to the network namespace of the thread that creates them.
NetNs opens a namespace file such as /run/netns/<name>, NetNs::run
executes a closure on a thread inside it, and L2tpHandle::new_in controls
the L2TP subsystem of that namespace. Entering a namespace needs
CAP_SYS_ADMIN.
L2tpHandle::new() retains the calling thread’s actual namespace, including
an inherited container namespace, for future event connections. Opening an
event connection from a different namespace enters that original namespace;
opening it from the same namespace needs no additional privileges.
Handle and PPP object creation retain descriptors opened through
/proc/thread-self/ns/net, so that path must be accessible.
The kernel’s cross-namespace multicast behavior described under
Events still applies.
use l2tp::{L2tpHandle, NetNs, TunnelConfig, TunnelSocket};
async fn in_namespace(cfg: TunnelConfig) -> l2tp::Result<()> {
let netns = NetNs::open("/run/netns/blue")?;
let handle = L2tpHandle::new_in(&netns).await?;
let socket_cfg = cfg.clone();
let socket = netns.run(move || TunnelSocket::for_config(&socket_cfg, None))??;
let _tunnel = handle.create_tunnel(cfg, socket).await?;
Ok(())
}§Interface Names
Interface names are L2tpIfName, the checked, Copy type of
netlink-packet-l2tp that this crate re-exports. It stores the kernel’s
NUL-padded char ifname[IFNAMSIZ] buffer and applies the kernel’s
dev_valid_name() rules: 1 to 15 bytes, not . or .., and no NUL, /,
: or whitespace. Construct one with L2tpIfName::new, str::parse or
TryFrom, read it back with as_bytes, as_array, to_str,
to_string_lossy or Display, and convert it with the L2tpIfNameExt
extension trait: from_name validates a string or bytes and reports a
rejected name as Error::InvalidIfName, index and from_index
map names to the kernel’s interface indexes, and as_os_str yields an
OsStr. Names are byte strings and need not be UTF-8. The upstream
constructors return DecodeError, re-exported here as l2tp::DecodeError,
which converts into Error::Netlink. The CLI validates --ifname and
--device while parsing and accepts non-UTF-8 arguments.
A session’s name is the one its interface had when the session was created;
the kernel does not follow renames. SessionHandle::ifindex identifies the
device for its whole life; see Kernel Semantics Notes.
§Sockets
TunnelSocket::for_configcreates the socket aTunnelConfigdescribes;TunnelSocket::udpandTunnelSocket::ipare the lower-level constructors.TunnelSocket::from_fdadopts a bound and connected datagram socket prepared elsewhere, for example withsocket2or through socket activation, andTunnelConfig::for_socketdescribes such a socket. The socket’s addresses, bound device, link-local scope and peer connection ID are read back from the kernel. A descriptor that is not such a socket is closed together with the error.set_tos,set_hop_limit,set_mark,set_pmtu_discoveryandset_ipv6_dontfragcover the common options of tunnel traffic;bind_to_deviceappliesSO_BINDTODEVICE, which needsCAP_NET_RAW.- A
deviceis not a scope. The kernel routes through the bound device by itself, so the socket never resolves it to an index; a device that disappears only matters to the kernel. A link-local scope, by contrast, is resolved to the interface index whenever a link-local address is bound or connected, and reconnecting to such an address fails withENODEVwhile the scope’s interface is gone. Other addresses need no scope and resolve none. disconnectissuesconnect(AF_UNSPEC): the kernel forgets the peer, the source address it selected andSO_BINDTODEVICE, while an L2TP/IP socket’s connection ID stays bound. Bind the device again if needed, thenreconnect_iporreconnect_udpto have the source address selected afresh, for example after the addresses of the underlying interface changed.local_addr_udp,peer_addr_udp,local_addr_ipandpeer_addr_ipreport the current addresses.- The kernel delivers L2TP control messages on the tunnel socket.
TunnelSocketimplementsAsFd, andtry_clone_fdduplicates the descriptor so the control plane can wrap it in an async UDP socket.
§Kernel Semantics Notes
L2TP_ATTR_IFNAMEapplies to sessions (SESSION_CREATE/session lookups), not tunnels. Tunnels do not create a netdevice. Sessions can be looked up, modified and deleted by interface name.- The session interface name is frozen at creation. The kernel copies the
l2tpethdevice’s name into the session once, right after registering the device, and never updates it. An interface renamed later, byip link, udev or systemd-networkd, is still reported bySessionInfo::ifnameand matched by the*_by_ifnamemethods under its original name, never its current one. Track these devices by interface index instead:SessionHandle::ifindexcarries it, and rtnetlink addresses a device by index for everything including renames. Only L2TPv3 sessions are searched by name. - L2TP netlink does not report the index, and a lookup by name after the
fact can be defeated by udev or networkd renaming the new device at once.
create_sessiontherefore joinsRTNLGRP_LINKbefore it sendsSESSION_CREATE: the kernel broadcasts the device’s registration asRTM_NEWLINKwith change mask~0(renames use0) before it acknowledges the creation, so the notification naming the new interface, with its index, is already queued when the session exists. Only a lost notification falls back to a lookup by name, as theopen_*methods always do. Request an explicit, uniqueSessionConfig::ifnamerather than the kernel’sl2tpeth%dpool, so no other device can hold the name while this one does. - Tunnel deletion is acknowledged before the kernel tears the tunnel down on
a work queue.
delete_tunnelandTunnelHandle::deletewait until the tunnel is gone, so its identifier can be reused immediately afterwards. If it is still listed after about two seconds they returnError::DeletionPending; the deletion is not undone, so treat the tunnel as deleted (a repeated deletion fails withENODEV). - Generic netlink assigns the
l2tpfamily identifier dynamically. A handle caches it and re-resolves once when the kernel answersENOENTfor a request, so it survives a reload ofl2tp_netlink; anEventsstream does not, so subscribe again after a module reload. - Lookups of a missing tunnel or session fail with
ENODEV. - The kernel never records a peer port for IPv4 L2TP/IP sockets, so
getpeernamereportsENOTCONNfor them.TunnelSocket::peer_addr_iptherefore reports the peer the socket connected to itself, which an IPv4 L2TP/IP socket adopted withfrom_fddoes not know: describe such a socket withTunnelConfig::newinstead ofTunnelConfig::for_socket. - The kernel accepts the L2-specific sublayer type (
L2TP_ATTR_L2SPEC_TYPE) when a session is created but never reports it, soSessionInfo::l2spec_typeisNoneand the CLI showsunknown. SessionConfig::eth,SessionConfig::pppand the CLI default the L2-specific sublayer toL2SpecType::None. The kernel and iproute2 default toL2SpecType::Default; setl2spec_typetoL2SpecType::Default(CLI:--l2spec default) when matching a peer configured with that default. Both peers must use the same type for their session wire formats to match.- The kernel reports the IPv4 UDP checksum setting (
L2TP_ATTR_UDP_CSUM) only for IPv4 sockets.TunnelInfotherefore showsudp_csum: falsefor every IPv6 tunnel, even when its IPv4-mapped peer receives checksums. - The kernel does not report IPv6 scope IDs or flow labels, so
TunnelInfoUDP endpoints have them set to0and IP endpoints have no scope. - Managed tunnels are fd-backed. When the managed tunnel socket is closed, the kernel
tears down that tunnel and its sessions. Sockets are created with
SOCK_CLOEXECso child processes do not keep tunnels alive. - The kernel cannot change tunnel parameters after creation, so there is no tunnel
modify operation. Session parameters can be changed with
SessionHandle::modify. - The kernel’s session dump ignores the tunnel filter;
list_sessionsfilters the dump on the client side.
§Unmanaged Tunnels
For kernel-managed tunnels without L2TP_ATTR_FD:
let cfg = TunnelConfig::new(/* ... */)?;
let tunnel = handle.create_unmanaged_tunnel(cfg).await?;§Common Operations
let tunnels = handle.list_tunnels().await?;
let sessions = handle.list_all_sessions().await?;
let tstats = handle.tunnel_stats(TunnelId(1000)).await?;
let sstats = handle.session_stats(TunnelId(1000), SessionId(3000)).await?;
// Existing objects, for example created by another process.
let tunnel = handle.open_tunnel(TunnelId(1000)).await?;
let session = handle.open_session_by_ifname(&"l2tpeth0".parse()?).await?;§Handle Lifetime
TunnelHandleandSessionHandlereturned by thecreate_*methods default toauto_delete = true; those returned by theopen_*methods default tofalse.- Tunnel and session creation keeps ownership through the kernel acknowledgement even if the caller is cancelled. Successful creations that the caller no longer receives are cleaned up. Keep the Tokio runtime running until this work finishes.
- On drop, they spawn best-effort async delete calls onto the current Tokio runtime. These run after the drop returns, so they can race with re-creating the same ID.
- Prefer
tunnel.delete().await?/session.delete().await?, which delete deterministically and report errors. - Use
set_auto_delete(false)if you want to avoid those best-effort netlink delete calls. - For managed tunnels, dropping the handle still closes the owned tunnel socket fd, which causes the kernel to remove the tunnel/session datapath.
§Error Handling
Match kernel errors by kind without depending on libc:
match handle.create_unmanaged_tunnel(cfg).await {
Err(e) if e.kind() == Some(std::io::ErrorKind::AlreadyExists) => { /* ID in use */ }
other => { /* ... */ }
}Error::raw_os_error() returns the raw errno when one is available. No
public function panics on its input; conditions such as a missing Tokio I/O
driver, an invalid interface name or an unsupported protocol version are
reported as errors.
§Logging
The crate logs netlink requests and failures through the log crate at
debug and trace level.
§Upgrading from 0.2
0.3.0 was never published; its changes ship in 0.4.0.
- The
l2tpbinary now requires--features cli(see above). serdederives on the statistics types now require theserdefeature.- Modules are private; import everything from the crate root (e.g.
l2tp::L2tpHandle). TunnelModify,TunnelHandle::modifyandL2tpHandle::modify_tunnelwere removed: the kernel accepted these requests but did not apply their UDP checksum setting.TunnelSocket::iptakesTunnelIds for the tunnel and its peer instead of au32.delete_tunnelandTunnelHandle::deletewait, for up to about two seconds, until the kernel has removed the tunnel. A tunnel still listed after that isError::DeletionPending, which callers must treat as deleted: the acknowledged deletion completes on its own, and repeating it fails withENODEV. Code that kept its tunnel state on any error fromdelete_tunnelwould otherwise never remove it.SessionInfo::ifnameand the*_by_ifnamemethods carry the creation-time name only; rename and otherwise addressl2tpethdevices bySessionHandle::ifindex. See Kernel Semantics Notes.- Reconnecting an L2TP/IP socket resolves its link-local scope only when a
link-local address is involved. A socket created with a
deviceno longer needs that device to exist to reconnect to a global address, anddisconnectforgets the device as the kernel does; callbind_to_deviceagain before reconnecting if the binding is still wanted. IpEndpoint::V6is a struct variant with anaddrand an optional interfacescope; useIpEndpoint::v6(addr)orIpEndpoint::v6_scoped(addr, ifname). Link-local endpoints without a scope are rejected withError::ScopeRequiredbyTunnelSocket::ipandTunnelSocket::udp, and unmanaged tunnels reject link-local endpoints withError::UnmanagedLinkLocal.- Interface names are
L2tpIfNamefromnetlink-packet-l2tp; the crate’s ownIfNameis gone, andL2tpIfNameExtadds the OS conversions. TunnelInfo::proto_versionisTunnelInfo::version, anL2tpVersion.SessionInfogainedpeer_tunnel_id,cookieandpeer_cookie, and itsl2spec_typeis anOptionthat isNonewhen the kernel does not report the sublayer type, which current kernels never do.CookieisCopy, parses from hexadecimal, and is built withTryFromorFrom<[u8; 4]>/From<[u8; 8]>instead oftry_from_bytes.L2tpHandle::newreturnsError::Ioinstead of panicking when the Tokio runtime has no I/O driver.Erroris#[non_exhaustive].Error::Netlinkholds a message instead of anetlink-packet-coretype and also reports invalid attribute values such as interface names. Malformed kernel replies are reported asError::UnexpectedReplyinstead of a syntheticKernelError, and socket encapsulation mismatches asError::EncapsulationMismatchinstead ofError::UnmanagedSocket.TunnelSocket::set_ipv6_dontfragerrors areError::Io.PseudowireTypeandL2SpecTypeare#[non_exhaustive]and haveOther(..)variants for values this crate does not name.TunnelInfo,SessionInfo,TunnelStatsandSessionStatsare#[non_exhaustive].
§Running Tests
Standard tests:
cargo testPrivileged tests (require root/CAP_NET_ADMIN and matching kernel modules):
cargo test --test privileged -- --ignored§CLI tunnel lifetime
l2tp tunnel create creates an unmanaged, kernel-owned tunnel that survives
command exit. Remove it with l2tp tunnel delete. Checksum flags apply to the
kernel-created socket. The socket-only flags --device and --ipv6-dontfrag
are unsupported by persistent CLI tunnels and return an error; use the library’s
managed tunnel API when those socket options are needed.
--no-udp-csum is rejected when the remote peer is IPv4-mapped on an IPv6
socket, including when an IPv4 --remote is automatically mapped to match an
IPv6 --local. Use IPv4 endpoints for both addresses, or the library’s managed
tunnel API, to disable IPv4 checksums. Mapped tunnels with checksums enabled
remain supported.
Link-local addresses, with or without a %ifname zone suffix, are rejected
for the same reason: the kernel-created socket has no interface scope. Use the
library’s managed tunnel API for link-local endpoints.
Automatically chosen tunnel IDs come from getrandom(2); when that fails the
command reports the error instead of falling back to a predictable source.
Pass --tunnel-id to choose the ID yourself.
All public types are re-exported at the crate root.
The API is only available on Linux; on other platforms this crate is empty.
Modules§
- blocking
- Blocking counterparts of the async API.
Structs§
- Cookie
- Session cookie, 0, 4 or 8 bytes (
L2TP_ATTR_COOKIE). - Decode
Error - The error of
L2tpIfName’s own constructors (new,parseand theTryFromconversions), re-exported so it can be named without depending onnetlink-packet-core; it converts intoError::Netlink.L2tpIfNameExt::from_namereports the same failures asError::InvalidIfName. - Events
- Stream of
L2tpEvents from the kernel, created byL2tpHandle::events. - L2tp
Handle - Shared async handle for L2TP generic-netlink operations.
- L2tp
IfName - A Linux network interface name, as carried by
L2TP_ATTR_IFNAME. - NetNs
- An open network namespace, such as
/run/netns/<name>or/proc/<pid>/ns/net. - PppChannel
- A PPP channel attached to a
PppUnit; dropping it disconnects the channel from the unit (PPPIOCDISCONN), so the channel can be connected to another unit while itsPppSocketlives on. Closing the descriptor alone would leave the channel connected. - PppSocket
- A connected
PPPoL2TPsocket: the data plane of a PPP pseudowire. - PppUnit
- A PPP network interface (
pppN) created through/dev/ppp. - Session
Config - Parameters for creating an L2TP session.
- Session
Handle - Handle for managing a session lifecycle.
- Session
Id - Kernel session identifier (
L2TP_ATTR_SESSION_ID). - Session
Info - Runtime information returned by the kernel for a session.
- Session
Modify - Mutable session parameters.
- Session
Stats - Session traffic/error counters.
- Tunnel
Config - Parameters for creating an L2TP tunnel.
- Tunnel
Handle - Handle for managing a tunnel lifecycle.
- Tunnel
Id - Kernel tunnel identifier (
L2TP_ATTR_CONN_ID). - Tunnel
Info - Runtime information returned by the kernel for a tunnel.
- Tunnel
Socket - Owned tunnel socket used by managed tunnels (
L2TP_ATTR_FD). - Tunnel
Stats - Tunnel traffic/error counters.
Enums§
- Encapsulation
- Tunnel encapsulation parameters.
- Error
- Errors returned by this crate.
- IpEndpoint
- IP endpoint used for IP encapsulated (
L2TPIP) tunnels. - L2Spec
Type - L2-specific sublayer header type.
- L2tp
Event - A change the kernel announced on the
l2tpmulticast group. - L2tp
Version - L2TP protocol version of a tunnel (
L2TP_ATTR_PROTO_VERSION). - Pmtu
Discovery - Path MTU discovery mode of a tunnel socket (
IP_MTU_DISCOVERandIPV6_MTU_DISCOVER). - Pseudowire
Type - Session pseudowire type.
- UdpEndpoint
- UDP socket endpoint used for UDP encapsulated tunnels.
Constants§
- IFNAMSIZ
- Size of the kernel’s interface name buffer (
IFNAMSIZ), including the NUL terminator; the length ofL2tpIfName::as_array.
Traits§
- L2tp
IfName Ext - Operating-system conversions for
L2tpIfName, the interface name type ofnetlink-packet-l2tpthat this crate re-exports.
Type Aliases§
- Result
- Crate-local result type.