forked from Minki/linux
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: Chris Elston <celston@katalix.com> Signed-off-by: James Chapman <jchapman@katalix.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
2121c3f571
commit
f9bac8df90
@ -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,
|
||||
};
|
||||
|
||||
|
@ -1366,31 +1366,68 @@ 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;
|
||||
@ -1398,6 +1435,13 @@ static int l2tp_tunnel_sock_create(u32 tunnel_id, u32 peer_tunnel_id, struct l2t
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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])
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
@ -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;
|
||||
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user