Commit f9bac8df authored by Chris Elston's avatar Chris Elston Committed by David S. Miller

l2tp: netlink api for l2tpv3 ipv6 unmanaged tunnels

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: default avatarChris Elston <celston@katalix.com>
Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2121c3f5
...@@ -108,6 +108,8 @@ enum { ...@@ -108,6 +108,8 @@ enum {
L2TP_ATTR_MTU, /* u16 */ L2TP_ATTR_MTU, /* u16 */
L2TP_ATTR_MRU, /* u16 */ L2TP_ATTR_MRU, /* u16 */
L2TP_ATTR_STATS, /* nested */ L2TP_ATTR_STATS, /* nested */
L2TP_ATTR_IP6_SADDR, /* struct in6_addr */
L2TP_ATTR_IP6_DADDR, /* struct in6_addr */
__L2TP_ATTR_MAX, __L2TP_ATTR_MAX,
}; };
......
...@@ -1366,11 +1366,44 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t ...@@ -1366,11 +1366,44 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
{ {
int err = -EINVAL; int err = -EINVAL;
struct sockaddr_in udp_addr; struct sockaddr_in udp_addr;
#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 udp6_addr;
#endif
struct sockaddr_l2tpip ip_addr; struct sockaddr_l2tpip ip_addr;
struct socket *sock = NULL; struct socket *sock = NULL;
switch (cfg->encap) { switch (cfg->encap) {
case L2TP_ENCAPTYPE_UDP: case L2TP_ENCAPTYPE_UDP:
#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;
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;
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); err = sock_create(AF_INET, SOCK_DGRAM, 0, sockp);
if (err < 0) if (err < 0)
goto out; goto out;
...@@ -1381,16 +1414,20 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t ...@@ -1381,16 +1414,20 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
udp_addr.sin_family = AF_INET; udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->local_ip; udp_addr.sin_addr = cfg->local_ip;
udp_addr.sin_port = htons(cfg->local_udp_port); udp_addr.sin_port = htons(cfg->local_udp_port);
err = kernel_bind(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); err = kernel_bind(sock, (struct sockaddr *) &udp_addr,
sizeof(udp_addr));
if (err < 0) if (err < 0)
goto out; goto out;
udp_addr.sin_family = AF_INET; udp_addr.sin_family = AF_INET;
udp_addr.sin_addr = cfg->peer_ip; udp_addr.sin_addr = cfg->peer_ip;
udp_addr.sin_port = htons(cfg->peer_udp_port); udp_addr.sin_port = htons(cfg->peer_udp_port);
err = kernel_connect(sock, (struct sockaddr *) &udp_addr, sizeof(udp_addr), 0); err = kernel_connect(sock,
(struct sockaddr *) &udp_addr,
sizeof(udp_addr), 0);
if (err < 0) if (err < 0)
goto out; goto out;
}
if (!cfg->use_udp_checksums) if (!cfg->use_udp_checksums)
sock->sk->sk_no_check = UDP_CSUM_NOXMIT; sock->sk->sk_no_check = UDP_CSUM_NOXMIT;
...@@ -1398,6 +1435,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t ...@@ -1398,6 +1435,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
break; break;
case L2TP_ENCAPTYPE_IP: 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); err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_L2TP, sockp);
if (err < 0) if (err < 0)
goto out; goto out;
......
...@@ -151,6 +151,10 @@ struct l2tp_tunnel_cfg { ...@@ -151,6 +151,10 @@ struct l2tp_tunnel_cfg {
/* Used only for kernel-created sockets */ /* Used only for kernel-created sockets */
struct in_addr local_ip; struct in_addr local_ip;
struct in_addr peer_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 local_udp_port;
u16 peer_udp_port; u16 peer_udp_port;
unsigned int use_udp_checksums:1; unsigned int use_udp_checksums:1;
......
...@@ -133,10 +133,25 @@ static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info ...@@ -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]) { if (info->attrs[L2TP_ATTR_FD]) {
fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]); fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
} else { } else {
if (info->attrs[L2TP_ATTR_IP_SADDR]) #if IS_ENABLED(CONFIG_IPV6)
cfg.local_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_SADDR]); if (info->attrs[L2TP_ATTR_IP6_SADDR] &&
if (info->attrs[L2TP_ATTR_IP_DADDR]) info->attrs[L2TP_ATTR_IP6_DADDR]) {
cfg.peer_ip.s_addr = nla_get_be32(info->attrs[L2TP_ATTR_IP_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]) if (info->attrs[L2TP_ATTR_UDP_SPORT])
cfg.local_udp_port = nla_get_u16(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]) if (info->attrs[L2TP_ATTR_UDP_DPORT])
...@@ -225,6 +240,9 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, ...@@ -225,6 +240,9 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
struct nlattr *nest; struct nlattr *nest;
struct sock *sk = NULL; struct sock *sk = NULL;
struct inet_sock *inet; struct inet_sock *inet;
#if IS_ENABLED(CONFIG_IPV6)
struct ipv6_pinfo *np = NULL;
#endif
struct l2tp_stats stats; struct l2tp_stats stats;
unsigned int start; unsigned int start;
...@@ -273,6 +291,11 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, ...@@ -273,6 +291,11 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
if (!sk) if (!sk)
goto out; goto out;
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == AF_INET6)
np = inet6_sk(sk);
#endif
inet = inet_sk(sk); inet = inet_sk(sk);
switch (tunnel->encap) { switch (tunnel->encap) {
...@@ -284,6 +307,15 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags, ...@@ -284,6 +307,15 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
goto nla_put_failure; goto nla_put_failure;
/* NOBREAK */ /* NOBREAK */
case L2TP_ENCAPTYPE_IP: 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) || if (nla_put_be32(skb, L2TP_ATTR_IP_SADDR, inet->inet_saddr) ||
nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr)) nla_put_be32(skb, L2TP_ATTR_IP_DADDR, inet->inet_daddr))
goto nla_put_failure; goto nla_put_failure;
...@@ -752,6 +784,14 @@ static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = { ...@@ -752,6 +784,14 @@ static struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
[L2TP_ATTR_MTU] = { .type = NLA_U16, }, [L2TP_ATTR_MTU] = { .type = NLA_U16, },
[L2TP_ATTR_MRU] = { .type = NLA_U16, }, [L2TP_ATTR_MRU] = { .type = NLA_U16, },
[L2TP_ATTR_STATS] = { .type = NLA_NESTED, }, [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] = { [L2TP_ATTR_IFNAME] = {
.type = NLA_NUL_STRING, .type = NLA_NUL_STRING,
.len = IFNAMSIZ - 1, .len = IFNAMSIZ - 1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment