Skip to content

Commit

Permalink
l2tp: netlink api for l2tpv3 ipv6 unmanaged tunnels
Browse files Browse the repository at this point in the history
This patch adds support for unmanaged L2TPv3 tunnels over IPv6 using
the netlink API. We already support unmanaged L2TPv3 tunnels over
IPv4. A patch to iproute2 to make use of this feature will be
submitted separately.

Signed-off-by: Chris Elston <celston@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Chris Elston authored and David S. Miller committed May 1, 2012
1 parent 2121c3f commit f9bac8d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 21 deletions.
2 changes: 2 additions & 0 deletions include/linux/l2tp.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ enum {
L2TP_ATTR_MTU, /* u16 */
L2TP_ATTR_MRU, /* u16 */
L2TP_ATTR_STATS, /* nested */
L2TP_ATTR_IP6_SADDR, /* struct in6_addr */
L2TP_ATTR_IP6_DADDR, /* struct in6_addr */
__L2TP_ATTR_MAX,
};

Expand Down
78 changes: 61 additions & 17 deletions net/l2tp/l2tp_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1366,38 +1366,82 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
{
int err = -EINVAL;
struct sockaddr_in udp_addr;
#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 udp6_addr;
#endif
struct sockaddr_l2tpip ip_addr;
struct socket *sock = NULL;

switch (cfg->encap) {
case L2TP_ENCAPTYPE_UDP:
err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp);
if (err < 0)
goto out;
#if IS_ENABLED(CONFIG_IPV6)
if (cfg->local_ip6 && cfg->peer_ip6) {
err = sock_create(AF_INET6, SOCK_DGRAM, 0, sockp);
if (err < 0)
goto out;

sock = *sockp;
sock = *sockp;

memset(&udp_addr, 0, sizeof(udp_addr));
udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->local_ip;
udp_addr.sin_port = htons(cfg->local_udp_port);
err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr));
if (err < 0)
goto out;
memset(&udp6_addr, 0, sizeof(udp6_addr));
udp6_addr.sin6_family = AF_INET6;
memcpy(&udp6_addr.sin6_addr, cfg->local_ip6,
sizeof(udp6_addr.sin6_addr));
udp6_addr.sin6_port = htons(cfg->local_udp_port);
err = kernel_bind(sock, (struct sockaddr *) &udp6_addr,
sizeof(udp6_addr));
if (err < 0)
goto out;

udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->peer_ip;
udp_addr.sin_port = htons(cfg->peer_udp_port);
err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0);
if (err < 0)
goto out;
udp6_addr.sin6_family = AF_INET6;
memcpy(&udp6_addr.sin6_addr, cfg->peer_ip6,
sizeof(udp6_addr.sin6_addr));
udp6_addr.sin6_port = htons(cfg->peer_udp_port);
err = kernel_connect(sock,
(struct sockaddr *) &udp6_addr,
sizeof(udp6_addr), 0);
if (err < 0)
goto out;
} else
#endif
{
err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp);
if (err < 0)
goto out;

sock = *sockp;

memset(&udp_addr, 0, sizeof(udp_addr));
udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->local_ip;
udp_addr.sin_port = htons(cfg->local_udp_port);
err = kernel_bind(sock, (struct sockaddr *) &udp_addr,
sizeof(udp_addr));
if (err < 0)
goto out;

udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->peer_ip;
udp_addr.sin_port = htons(cfg->peer_udp_port);
err = kernel_connect(sock,
(struct sockaddr *) &udp_addr,
sizeof(udp_addr), 0);
if (err < 0)
goto out;
}

if (!cfg->use_udp_checksums)
sock->sk->sk_no_check = UDP_CSUM_NOXMIT;

break;

case L2TP_ENCAPTYPE_IP:
#if IS_ENABLED(CONFIG_IPV6)
if (cfg->local_ip6 && cfg->peer_ip6) {
/* IP encap over IPv6 not yet supported */
err = -EPROTONOSUPPORT;
goto out;
}
#endif
err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp);
if (err < 0)
goto out;
Expand Down
4 changes: 4 additions & 0 deletions net/l2tp/l2tp_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ struct l2tp_tunnel_cfg {
/* Used only for kernel-created sockets */
struct in_addr local_ip;
struct in_addr peer_ip;
#if IS_ENABLED(CONFIG_IPV6)
struct in6_addr *local_ip6;
struct in6_addr *peer_ip6;
#endif
u16 local_udp_port;
u16 peer_udp_port;
unsigned int use_udp_checksums:1;
Expand Down
48 changes: 44 additions & 4 deletions net/l2tp/l2tp_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,25 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info
if (info->attrs[L2TP_ATTR_FD]) {
fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
} else {
if (info->attrs[L2TP_ATTR_IP_SADDR])
cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]);
if (info->attrs[L2TP_ATTR_IP_DADDR])
cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_DADDR]);
#if IS_ENABLED(CONFIG_IPV6)
if (info->attrs[L2TP_ATTR_IP6_SADDR] &&
info->attrs[L2TP_ATTR_IP6_DADDR]) {
cfg.local_ip6 = nla_data(
info->attrs[L2TP_ATTR_IP6_SADDR]);
cfg.peer_ip6 = nla_data(
info->attrs[L2TP_ATTR_IP6_DADDR]);
} else
#endif
if (info->attrs[L2TP_ATTR_IP_SADDR] &&
info->attrs[L2TP_ATTR_IP_DADDR]) {
cfg.local_ip.s_addr = nla_get_be32(
info->attrs[L2TP_ATTR_IP_SADDR]);
cfg.peer_ip.s_addr = nla_get_be32(
info->attrs[L2TP_ATTR_IP_DADDR]);
} else {
ret = -EINVAL;
goto out;
}
if (info->attrs[L2TP_ATTR_UDP_SPORT])
cfg.local_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_SPORT]);
if (info->attrs[L2TP_ATTR_UDP_DPORT])
Expand Down Expand Up @@ -225,6 +240,9 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
struct nlattr *nest;
struct sock *sk = NULL;
struct inet_sock *inet;
#if IS_ENABLED(CONFIG_IPV6)
struct ipv6_pinfo *np = NULL;
#endif
struct l2tp_stats stats;
unsigned int start;

Expand Down Expand Up @@ -273,6 +291,11 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
if (!sk)
goto out;

#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == AF_INET6)
np = inet6_sk(sk);
#endif

inet = inet_sk(sk);

switch (tunnel->encap) {
Expand All @@ -284,6 +307,15 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
goto nla_put_failure;
/* NOBREAK */
case L2TP_ENCAPTYPE_IP:
#if IS_ENABLED(CONFIG_IPV6)
if (np) {
if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr),
&np->saddr) ||
nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(np->daddr),
&np->daddr))
goto nla_put_failure;
} else
#endif
if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) ||
nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr))
goto nla_put_failure;
Expand Down Expand Up @@ -752,6 +784,14 @@ static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
[L2TP_ATTR_MTU] = { .type = NLA_U16, },
[L2TP_ATTR_MRU] = { .type = NLA_U16, },
[L2TP_ATTR_STATS] = { .type = NLA_NESTED, },
[L2TP_ATTR_IP6_SADDR] = {
.type = NLA_BINARY,
.len = sizeof(struct in6_addr),
},
[L2TP_ATTR_IP6_DADDR] = {
.type = NLA_BINARY,
.len = sizeof(struct in6_addr),
},
[L2TP_ATTR_IFNAME] = {
.type = NLA_NUL_STRING,
.len = IFNAMSIZ - 1,
Expand Down

0 comments on commit f9bac8d

Please sign in to comment.