2019-06-04 15:11:33 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2010-04-02 13:19:10 +07:00
|
|
|
/*
|
|
|
|
* L2TP netlink layer, for management
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008,2009,2010 Katalix Systems Ltd
|
|
|
|
*
|
|
|
|
* Partly based on the IrDA nelink implementation
|
|
|
|
* (see net/irda/irnetlink.c) which is:
|
|
|
|
* Copyright (c) 2007 Samuel Ortiz <samuel@sortiz.org>
|
|
|
|
* which is in turn partly based on the wireless netlink code:
|
|
|
|
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
|
|
|
*/
|
|
|
|
|
2012-05-16 16:55:56 +07:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
#include <net/sock.h>
|
|
|
|
#include <net/genetlink.h>
|
|
|
|
#include <net/udp.h>
|
|
|
|
#include <linux/in.h>
|
|
|
|
#include <linux/udp.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <net/net_namespace.h>
|
|
|
|
|
|
|
|
#include <linux/l2tp.h>
|
|
|
|
|
|
|
|
#include "l2tp_core.h"
|
|
|
|
|
|
|
|
|
2016-10-24 19:40:03 +07:00
|
|
|
static struct genl_family l2tp_nl_family;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
static const struct genl_multicast_group l2tp_multicast_group[] = {
|
|
|
|
{
|
|
|
|
.name = L2TP_GENL_MCGROUP,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq,
|
|
|
|
int flags, struct l2tp_tunnel *tunnel, u8 cmd);
|
|
|
|
static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq,
|
|
|
|
int flags, struct l2tp_session *session,
|
|
|
|
u8 cmd);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
/* Accessed under genl lock */
|
|
|
|
static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
|
|
|
|
|
2017-10-31 23:36:42 +07:00
|
|
|
static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info)
|
2010-04-02 13:19:10 +07:00
|
|
|
{
|
|
|
|
u32 tunnel_id;
|
|
|
|
u32 session_id;
|
|
|
|
char *ifname;
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
struct l2tp_session *session = NULL;
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_IFNAME]) {
|
|
|
|
ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
|
2017-10-31 23:36:42 +07:00
|
|
|
session = l2tp_session_get_by_ifname(net, ifname);
|
2010-04-02 13:19:10 +07:00
|
|
|
} else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
|
|
|
|
(info->attrs[L2TP_ATTR_CONN_ID])) {
|
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
|
|
|
session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
|
2017-08-25 21:51:40 +07:00
|
|
|
tunnel = l2tp_tunnel_get(net, tunnel_id);
|
|
|
|
if (tunnel) {
|
2018-08-10 18:21:57 +07:00
|
|
|
session = l2tp_tunnel_get_session(tunnel, session_id);
|
2017-08-25 21:51:40 +07:00
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
|
|
|
}
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return session;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
int ret = -ENOBUFS;
|
|
|
|
|
2012-06-28 10:57:45 +07:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (!msg) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
|
2010-04-02 13:19:10 +07:00
|
|
|
&l2tp_nl_family, 0, L2TP_CMD_NOOP);
|
2012-09-25 01:29:01 +07:00
|
|
|
if (!hdr) {
|
|
|
|
ret = -EMSGSIZE;
|
2010-04-02 13:19:10 +07:00
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
err_out:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
static int l2tp_tunnel_notify(struct genl_family *family,
|
|
|
|
struct genl_info *info,
|
|
|
|
struct l2tp_tunnel *tunnel,
|
|
|
|
u8 cmd)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
|
|
|
|
NLM_F_ACK, tunnel, cmd);
|
|
|
|
|
2016-02-15 10:24:44 +07:00
|
|
|
if (ret >= 0) {
|
|
|
|
ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
|
|
|
|
/* We don't care if no one is listening */
|
|
|
|
if (ret == -ESRCH)
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
2014-12-28 01:12:39 +07:00
|
|
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_session_notify(struct genl_family *family,
|
|
|
|
struct genl_info *info,
|
|
|
|
struct l2tp_session *session,
|
|
|
|
u8 cmd)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
|
|
|
|
NLM_F_ACK, session, cmd);
|
|
|
|
|
2016-02-15 10:24:44 +07:00
|
|
|
if (ret >= 0) {
|
|
|
|
ret = genlmsg_multicast_allns(family, msg, 0, 0, GFP_ATOMIC);
|
|
|
|
/* We don't care if no one is listening */
|
|
|
|
if (ret == -ESRCH)
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
2014-12-28 01:12:39 +07:00
|
|
|
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
static int l2tp_nl_cmd_tunnel_create(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
u32 tunnel_id;
|
|
|
|
u32 peer_tunnel_id;
|
|
|
|
int proto_version;
|
|
|
|
int fd;
|
|
|
|
int ret = 0;
|
|
|
|
struct l2tp_tunnel_cfg cfg = { 0, };
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_PEER_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
peer_tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_CONN_ID]);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_PROTO_VERSION]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
proto_version = nla_get_u8(info->attrs[L2TP_ATTR_PROTO_VERSION]);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_ENCAP_TYPE]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
cfg.encap = nla_get_u16(info->attrs[L2TP_ATTR_ENCAP_TYPE]);
|
|
|
|
|
2010-04-02 13:19:40 +07:00
|
|
|
fd = -1;
|
|
|
|
if (info->attrs[L2TP_ATTR_FD]) {
|
|
|
|
fd = nla_get_u32(info->attrs[L2TP_ATTR_FD]);
|
|
|
|
} else {
|
2012-04-30 04:48:52 +07:00
|
|
|
#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]) {
|
2015-03-29 21:59:26 +07:00
|
|
|
cfg.local_ip.s_addr = nla_get_in_addr(
|
2012-04-30 04:48:52 +07:00
|
|
|
info->attrs[L2TP_ATTR_IP_SADDR]);
|
2015-03-29 21:59:26 +07:00
|
|
|
cfg.peer_ip.s_addr = nla_get_in_addr(
|
2012-04-30 04:48:52 +07:00
|
|
|
info->attrs[L2TP_ATTR_IP_DADDR]);
|
|
|
|
} else {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-04-02 13:19:40 +07:00
|
|
|
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])
|
|
|
|
cfg.peer_udp_port = nla_get_u16(info->attrs[L2TP_ATTR_UDP_DPORT]);
|
2016-11-08 03:39:27 +07:00
|
|
|
cfg.use_udp_checksums = nla_get_flag(
|
|
|
|
info->attrs[L2TP_ATTR_UDP_CSUM]);
|
2014-05-23 22:47:40 +07:00
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
2016-11-08 03:39:27 +07:00
|
|
|
cfg.udp6_zero_tx_checksums = nla_get_flag(
|
|
|
|
info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_TX]);
|
|
|
|
cfg.udp6_zero_rx_checksums = nla_get_flag(
|
|
|
|
info->attrs[L2TP_ATTR_UDP_ZERO_CSUM6_RX]);
|
2014-05-23 22:47:40 +07:00
|
|
|
#endif
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_DEBUG])
|
|
|
|
cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
|
|
|
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
switch (cfg.encap) {
|
|
|
|
case L2TP_ENCAPTYPE_UDP:
|
|
|
|
case L2TP_ENCAPTYPE_IP:
|
|
|
|
ret = l2tp_tunnel_create(net, fd, proto_version, tunnel_id,
|
|
|
|
peer_tunnel_id, &cfg, &tunnel);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
l2tp: fix races in tunnel creation
l2tp_tunnel_create() inserts the new tunnel into the namespace's tunnel
list and sets the socket's ->sk_user_data field, before returning it to
the caller. Therefore, there are two ways the tunnel can be accessed
and freed, before the caller even had the opportunity to take a
reference. In practice, syzbot could crash the module by closing the
socket right after a new tunnel was returned to pppol2tp_create().
This patch moves tunnel registration out of l2tp_tunnel_create(), so
that the caller can safely hold a reference before publishing the
tunnel. This second step is done with the new l2tp_tunnel_register()
function, which is now responsible for associating the tunnel to its
socket and for inserting it into the namespace's list.
While moving the code to l2tp_tunnel_register(), a few modifications
have been done. First, the socket validation tests are done in a helper
function, for clarity. Also, modifying the socket is now done after
having inserted the tunnel to the namespace's tunnels list. This will
allow insertion to fail, without having to revert theses modifications
in the error path (a followup patch will check for duplicate tunnels
before insertion). Either the socket is a kernel socket which we
control, or it is a user-space socket for which we have a reference on
the file descriptor. In any case, the socket isn't going to be closed
from under us.
Reported-by: syzbot+fbeeb5c3b538e8545644@syzkaller.appspotmail.com
Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-04-11 02:01:12 +07:00
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
l2tp_tunnel_inc_refcount(tunnel);
|
|
|
|
ret = l2tp_tunnel_register(tunnel, net, &cfg);
|
|
|
|
if (ret < 0) {
|
|
|
|
kfree(tunnel);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = l2tp_tunnel_notify(&l2tp_nl_family, info, tunnel,
|
|
|
|
L2TP_CMD_TUNNEL_CREATE);
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_tunnel_delete(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
u32 tunnel_id;
|
|
|
|
int ret = 0;
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
|
|
|
|
2017-08-25 21:51:42 +07:00
|
|
|
tunnel = l2tp_tunnel_get(net, tunnel_id);
|
|
|
|
if (!tunnel) {
|
2010-04-02 13:19:10 +07:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
l2tp_tunnel_notify(&l2tp_nl_family, info,
|
|
|
|
tunnel, L2TP_CMD_TUNNEL_DELETE);
|
|
|
|
|
2017-10-25 20:57:55 +07:00
|
|
|
l2tp_tunnel_delete(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2017-08-25 21:51:42 +07:00
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_tunnel_modify(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
u32 tunnel_id;
|
|
|
|
int ret = 0;
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
|
|
|
|
2017-08-25 21:51:42 +07:00
|
|
|
tunnel = l2tp_tunnel_get(net, tunnel_id);
|
|
|
|
if (!tunnel) {
|
2010-04-02 13:19:10 +07:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_DEBUG])
|
|
|
|
tunnel->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
|
|
|
|
tunnel, L2TP_CMD_TUNNEL_MODIFY);
|
|
|
|
|
2017-08-25 21:51:42 +07:00
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
|
2014-12-28 01:12:39 +07:00
|
|
|
struct l2tp_tunnel *tunnel, u8 cmd)
|
2010-04-02 13:19:10 +07:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *nest;
|
|
|
|
struct sock *sk = NULL;
|
|
|
|
struct inet_sock *inet;
|
2012-04-30 04:48:52 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
struct ipv6_pinfo *np = NULL;
|
|
|
|
#endif
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
|
2012-09-25 01:29:01 +07:00
|
|
|
if (!hdr)
|
|
|
|
return -EMSGSIZE;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2012-04-02 06:59:31 +07:00
|
|
|
if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_DEBUG, tunnel->debug) ||
|
|
|
|
nla_put_u16(skb, L2TP_ATTR_ENCAP_TYPE, tunnel->encap))
|
|
|
|
goto nla_put_failure;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2019-04-26 16:13:06 +07:00
|
|
|
nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (nest == NULL)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2016-04-25 15:25:19 +07:00
|
|
|
if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
|
|
|
|
atomic_long_read(&tunnel->stats.tx_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
|
|
|
|
atomic_long_read(&tunnel->stats.tx_bytes),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
|
|
|
|
atomic_long_read(&tunnel->stats.tx_errors),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
|
|
|
|
atomic_long_read(&tunnel->stats.rx_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
|
|
|
|
atomic_long_read(&tunnel->stats.rx_bytes),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
|
|
|
|
atomic_long_read(&tunnel->stats.rx_seq_discards),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
|
|
|
|
atomic_long_read(&tunnel->stats.rx_oos_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
|
|
|
|
atomic_long_read(&tunnel->stats.rx_errors),
|
|
|
|
L2TP_ATTR_STATS_PAD))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
2010-04-02 13:19:10 +07:00
|
|
|
nla_nest_end(skb, nest);
|
|
|
|
|
|
|
|
sk = tunnel->sock;
|
|
|
|
if (!sk)
|
|
|
|
goto out;
|
|
|
|
|
2012-04-30 04:48:52 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
if (sk->sk_family == AF_INET6)
|
|
|
|
np = inet6_sk(sk);
|
|
|
|
#endif
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
inet = inet_sk(sk);
|
|
|
|
|
|
|
|
switch (tunnel->encap) {
|
|
|
|
case L2TP_ENCAPTYPE_UDP:
|
2016-11-08 03:39:25 +07:00
|
|
|
switch (sk->sk_family) {
|
|
|
|
case AF_INET:
|
|
|
|
if (nla_put_u8(skb, L2TP_ATTR_UDP_CSUM, !sk->sk_no_check_tx))
|
|
|
|
goto nla_put_failure;
|
|
|
|
break;
|
2016-11-08 03:39:26 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
case AF_INET6:
|
|
|
|
if (udp_get_no_check6_tx(sk) &&
|
|
|
|
nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_TX))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (udp_get_no_check6_rx(sk) &&
|
|
|
|
nla_put_flag(skb, L2TP_ATTR_UDP_ZERO_CSUM6_RX))
|
|
|
|
goto nla_put_failure;
|
|
|
|
break;
|
|
|
|
#endif
|
2016-11-08 03:39:25 +07:00
|
|
|
}
|
2012-04-02 06:59:31 +07:00
|
|
|
if (nla_put_u16(skb, L2TP_ATTR_UDP_SPORT, ntohs(inet->inet_sport)) ||
|
2016-11-08 03:39:25 +07:00
|
|
|
nla_put_u16(skb, L2TP_ATTR_UDP_DPORT, ntohs(inet->inet_dport)))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
2017-10-18 05:42:53 +07:00
|
|
|
/* fall through */
|
2010-04-02 13:19:10 +07:00
|
|
|
case L2TP_ENCAPTYPE_IP:
|
2012-04-30 04:48:52 +07:00
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
|
|
if (np) {
|
2015-03-29 21:59:25 +07:00
|
|
|
if (nla_put_in6_addr(skb, L2TP_ATTR_IP6_SADDR,
|
|
|
|
&np->saddr) ||
|
|
|
|
nla_put_in6_addr(skb, L2TP_ATTR_IP6_DADDR,
|
|
|
|
&sk->sk_v6_daddr))
|
2012-04-30 04:48:52 +07:00
|
|
|
goto nla_put_failure;
|
|
|
|
} else
|
|
|
|
#endif
|
2015-03-29 21:59:25 +07:00
|
|
|
if (nla_put_in_addr(skb, L2TP_ATTR_IP_SADDR,
|
|
|
|
inet->inet_saddr) ||
|
|
|
|
nla_put_in_addr(skb, L2TP_ATTR_IP_DADDR,
|
|
|
|
inet->inet_daddr))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
2010-04-02 13:19:10 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2015-01-17 04:09:00 +07:00
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return 0;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_tunnel_get(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
u32 tunnel_id;
|
|
|
|
int ret = -ENOBUFS;
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:43 +07:00
|
|
|
goto err;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
|
|
|
|
2012-06-28 10:57:45 +07:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (!msg) {
|
|
|
|
ret = -ENOMEM;
|
2017-08-25 21:51:43 +07:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
tunnel = l2tp_tunnel_get(net, tunnel_id);
|
|
|
|
if (!tunnel) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto err_nlmsg;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
|
2014-12-28 01:12:39 +07:00
|
|
|
NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (ret < 0)
|
2017-08-25 21:51:43 +07:00
|
|
|
goto err_nlmsg_tunnel;
|
|
|
|
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
return genlmsg_unicast(net, msg, info->snd_portid);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2017-08-25 21:51:43 +07:00
|
|
|
err_nlmsg_tunnel:
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
|
|
|
err_nlmsg:
|
2010-04-02 13:19:10 +07:00
|
|
|
nlmsg_free(msg);
|
2017-08-25 21:51:43 +07:00
|
|
|
err:
|
2010-04-02 13:19:10 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_tunnel_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
int ti = cb->args[0];
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
|
|
|
|
|
for (;;) {
|
2018-04-13 01:50:33 +07:00
|
|
|
tunnel = l2tp_tunnel_get_nth(net, ti);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (tunnel == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
if (l2tp_nl_tunnel_send(skb, NETLINK_CB(cb->skb).portid,
|
2010-04-02 13:19:10 +07:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2018-04-13 01:50:33 +07:00
|
|
|
tunnel, L2TP_CMD_TUNNEL_GET) < 0) {
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
goto out;
|
2018-04-13 01:50:33 +07:00
|
|
|
}
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
ti++;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
cb->args[0] = ti;
|
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
u32 tunnel_id = 0;
|
|
|
|
u32 session_id;
|
|
|
|
u32 peer_session_id;
|
|
|
|
int ret = 0;
|
|
|
|
struct l2tp_tunnel *tunnel;
|
|
|
|
struct l2tp_session *session;
|
|
|
|
struct l2tp_session_cfg cfg = { 0, };
|
|
|
|
struct net *net = genl_info_net(info);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_CONN_ID]) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2017-08-25 21:51:46 +07:00
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
|
2017-08-25 21:51:46 +07:00
|
|
|
tunnel = l2tp_tunnel_get(net, tunnel_id);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (!tunnel) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
|
|
|
|
|
|
|
|
if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
|
|
|
|
if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
2018-06-15 20:39:17 +07:00
|
|
|
/* L2TPv2 only accepts PPP pseudo-wires */
|
|
|
|
if (tunnel->version == 2 && cfg.pw_type != L2TP_PWTYPE_PPP) {
|
|
|
|
ret = -EPROTONOSUPPORT;
|
|
|
|
goto out_tunnel;
|
|
|
|
}
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
if (tunnel->version > 2) {
|
2018-01-17 05:01:54 +07:00
|
|
|
if (info->attrs[L2TP_ATTR_L2SPEC_TYPE]) {
|
2010-04-02 13:19:10 +07:00
|
|
|
cfg.l2specific_type = nla_get_u8(info->attrs[L2TP_ATTR_L2SPEC_TYPE]);
|
2018-01-17 05:01:54 +07:00
|
|
|
if (cfg.l2specific_type != L2TP_L2SPECTYPE_DEFAULT &&
|
|
|
|
cfg.l2specific_type != L2TP_L2SPECTYPE_NONE) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_tunnel;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cfg.l2specific_type = L2TP_L2SPECTYPE_DEFAULT;
|
|
|
|
}
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_COOKIE]) {
|
|
|
|
u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
|
|
|
|
if (len > 8) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
cfg.cookie_len = len;
|
|
|
|
memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
|
|
|
|
}
|
|
|
|
if (info->attrs[L2TP_ATTR_PEER_COOKIE]) {
|
|
|
|
u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
|
|
|
|
if (len > 8) {
|
|
|
|
ret = -EINVAL;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
cfg.peer_cookie_len = len;
|
|
|
|
memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
|
|
|
|
}
|
|
|
|
if (info->attrs[L2TP_ATTR_IFNAME])
|
|
|
|
cfg.ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_DEBUG])
|
|
|
|
cfg.debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_RECV_SEQ])
|
|
|
|
cfg.recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_SEND_SEQ])
|
|
|
|
cfg.send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_LNS_MODE])
|
|
|
|
cfg.lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
|
|
|
|
cfg.reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
|
|
|
|
|
2015-09-24 11:33:34 +07:00
|
|
|
#ifdef CONFIG_MODULES
|
|
|
|
if (l2tp_nl_cmd_ops[cfg.pw_type] == NULL) {
|
|
|
|
genl_unlock();
|
|
|
|
request_module("net-l2tp-type-%u", cfg.pw_type);
|
|
|
|
genl_lock();
|
|
|
|
}
|
|
|
|
#endif
|
2010-04-02 13:19:10 +07:00
|
|
|
if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
|
|
|
|
(l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
|
|
|
|
ret = -EPROTONOSUPPORT;
|
2017-08-25 21:51:46 +07:00
|
|
|
goto out_tunnel;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
2017-09-01 22:58:51 +07:00
|
|
|
ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel,
|
|
|
|
session_id,
|
|
|
|
peer_session_id,
|
|
|
|
&cfg);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
if (ret >= 0) {
|
2018-08-10 18:21:57 +07:00
|
|
|
session = l2tp_tunnel_get_session(tunnel, session_id);
|
2017-03-31 18:02:29 +07:00
|
|
|
if (session) {
|
2014-12-28 01:12:39 +07:00
|
|
|
ret = l2tp_session_notify(&l2tp_nl_family, info, session,
|
|
|
|
L2TP_CMD_SESSION_CREATE);
|
2017-03-31 18:02:29 +07:00
|
|
|
l2tp_session_dec_refcount(session);
|
|
|
|
}
|
2014-12-28 01:12:39 +07:00
|
|
|
}
|
|
|
|
|
2017-08-25 21:51:46 +07:00
|
|
|
out_tunnel:
|
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct l2tp_session *session;
|
|
|
|
u16 pw_type;
|
|
|
|
|
2017-10-31 23:36:42 +07:00
|
|
|
session = l2tp_nl_session_get(info);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (session == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
l2tp_session_notify(&l2tp_nl_family, info,
|
|
|
|
session, L2TP_CMD_SESSION_DELETE);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
pw_type = session->pwtype;
|
|
|
|
if (pw_type < __L2TP_PWTYPE_MAX)
|
|
|
|
if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
|
|
|
|
ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
|
|
|
|
|
2017-03-31 18:02:30 +07:00
|
|
|
l2tp_session_dec_refcount(session);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct l2tp_session *session;
|
|
|
|
|
2017-10-31 23:36:42 +07:00
|
|
|
session = l2tp_nl_session_get(info);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (session == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_DEBUG])
|
|
|
|
session->debug = nla_get_u32(info->attrs[L2TP_ATTR_DEBUG]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_RECV_SEQ])
|
|
|
|
session->recv_seq = nla_get_u8(info->attrs[L2TP_ATTR_RECV_SEQ]);
|
|
|
|
|
2014-03-06 17:14:30 +07:00
|
|
|
if (info->attrs[L2TP_ATTR_SEND_SEQ]) {
|
2010-04-02 13:19:10 +07:00
|
|
|
session->send_seq = nla_get_u8(info->attrs[L2TP_ATTR_SEND_SEQ]);
|
2014-03-06 17:14:30 +07:00
|
|
|
l2tp_session_set_header_len(session, session->tunnel->version);
|
|
|
|
}
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_LNS_MODE])
|
|
|
|
session->lns_mode = nla_get_u8(info->attrs[L2TP_ATTR_LNS_MODE]);
|
|
|
|
|
|
|
|
if (info->attrs[L2TP_ATTR_RECV_TIMEOUT])
|
|
|
|
session->reorder_timeout = nla_get_msecs(info->attrs[L2TP_ATTR_RECV_TIMEOUT]);
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
ret = l2tp_session_notify(&l2tp_nl_family, info,
|
|
|
|
session, L2TP_CMD_SESSION_MODIFY);
|
|
|
|
|
2017-03-31 18:02:30 +07:00
|
|
|
l2tp_session_dec_refcount(session);
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
out:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int flags,
|
2014-12-28 01:12:39 +07:00
|
|
|
struct l2tp_session *session, u8 cmd)
|
2010-04-02 13:19:10 +07:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *nest;
|
|
|
|
struct l2tp_tunnel *tunnel = session->tunnel;
|
|
|
|
|
2014-12-28 01:12:39 +07:00
|
|
|
hdr = genlmsg_put(skb, portid, seq, &l2tp_nl_family, flags, cmd);
|
2012-09-25 01:29:01 +07:00
|
|
|
if (!hdr)
|
|
|
|
return -EMSGSIZE;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2012-04-02 06:59:31 +07:00
|
|
|
if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_PEER_CONN_ID, tunnel->peer_tunnel_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_PEER_SESSION_ID,
|
|
|
|
session->peer_session_id) ||
|
|
|
|
nla_put_u32(skb, L2TP_ATTR_DEBUG, session->debug) ||
|
2018-08-03 17:38:39 +07:00
|
|
|
nla_put_u16(skb, L2TP_ATTR_PW_TYPE, session->pwtype))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-10-25 12:22:01 +07:00
|
|
|
if ((session->ifname[0] &&
|
2012-04-02 06:59:31 +07:00
|
|
|
nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
|
|
|
|
(session->cookie_len &&
|
|
|
|
nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
|
|
|
|
&session->cookie[0])) ||
|
|
|
|
(session->peer_cookie_len &&
|
|
|
|
nla_put(skb, L2TP_ATTR_PEER_COOKIE, session->peer_cookie_len,
|
|
|
|
&session->peer_cookie[0])) ||
|
|
|
|
nla_put_u8(skb, L2TP_ATTR_RECV_SEQ, session->recv_seq) ||
|
|
|
|
nla_put_u8(skb, L2TP_ATTR_SEND_SEQ, session->send_seq) ||
|
|
|
|
nla_put_u8(skb, L2TP_ATTR_LNS_MODE, session->lns_mode) ||
|
2018-08-10 18:21:55 +07:00
|
|
|
(l2tp_tunnel_uses_xfrm(tunnel) &&
|
2012-04-02 06:59:31 +07:00
|
|
|
nla_put_u8(skb, L2TP_ATTR_USING_IPSEC, 1)) ||
|
|
|
|
(session->reorder_timeout &&
|
2016-04-22 22:31:21 +07:00
|
|
|
nla_put_msecs(skb, L2TP_ATTR_RECV_TIMEOUT,
|
|
|
|
session->reorder_timeout, L2TP_ATTR_PAD)))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
2012-04-30 04:48:46 +07:00
|
|
|
|
2019-04-26 16:13:06 +07:00
|
|
|
nest = nla_nest_start_noflag(skb, L2TP_ATTR_STATS);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (nest == NULL)
|
|
|
|
goto nla_put_failure;
|
2012-04-30 04:48:46 +07:00
|
|
|
|
2016-04-25 15:25:19 +07:00
|
|
|
if (nla_put_u64_64bit(skb, L2TP_ATTR_TX_PACKETS,
|
|
|
|
atomic_long_read(&session->stats.tx_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_TX_BYTES,
|
|
|
|
atomic_long_read(&session->stats.tx_bytes),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_TX_ERRORS,
|
|
|
|
atomic_long_read(&session->stats.tx_errors),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_PACKETS,
|
|
|
|
atomic_long_read(&session->stats.rx_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_BYTES,
|
|
|
|
atomic_long_read(&session->stats.rx_bytes),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_SEQ_DISCARDS,
|
|
|
|
atomic_long_read(&session->stats.rx_seq_discards),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_OOS_PACKETS,
|
|
|
|
atomic_long_read(&session->stats.rx_oos_packets),
|
|
|
|
L2TP_ATTR_STATS_PAD) ||
|
|
|
|
nla_put_u64_64bit(skb, L2TP_ATTR_RX_ERRORS,
|
|
|
|
atomic_long_read(&session->stats.rx_errors),
|
|
|
|
L2TP_ATTR_STATS_PAD))
|
2012-04-02 06:59:31 +07:00
|
|
|
goto nla_put_failure;
|
2010-04-02 13:19:10 +07:00
|
|
|
nla_nest_end(skb, nest);
|
|
|
|
|
2015-01-17 04:09:00 +07:00
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return 0;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct l2tp_session *session;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
int ret;
|
|
|
|
|
2017-10-31 23:36:42 +07:00
|
|
|
session = l2tp_nl_session_get(info);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (session == NULL) {
|
|
|
|
ret = -ENODEV;
|
2017-03-31 18:02:30 +07:00
|
|
|
goto err;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
2012-06-28 10:57:45 +07:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (!msg) {
|
|
|
|
ret = -ENOMEM;
|
2017-03-31 18:02:30 +07:00
|
|
|
goto err_ref;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
|
2014-12-28 01:12:39 +07:00
|
|
|
0, session, L2TP_CMD_SESSION_GET);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (ret < 0)
|
2017-03-31 18:02:30 +07:00
|
|
|
goto err_ref_msg;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2017-03-31 18:02:30 +07:00
|
|
|
ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2017-03-31 18:02:30 +07:00
|
|
|
l2tp_session_dec_refcount(session);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
2017-03-31 18:02:30 +07:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
err_ref_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
err_ref:
|
|
|
|
l2tp_session_dec_refcount(session);
|
|
|
|
err:
|
2010-04-02 13:19:10 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
|
struct l2tp_session *session;
|
|
|
|
struct l2tp_tunnel *tunnel = NULL;
|
|
|
|
int ti = cb->args[0];
|
|
|
|
int si = cb->args[1];
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (tunnel == NULL) {
|
2018-04-13 01:50:33 +07:00
|
|
|
tunnel = l2tp_tunnel_get_nth(net, ti);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (tunnel == NULL)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-10-31 23:36:42 +07:00
|
|
|
session = l2tp_session_get_nth(tunnel, si);
|
2010-04-02 13:19:10 +07:00
|
|
|
if (session == NULL) {
|
|
|
|
ti++;
|
2018-04-13 01:50:33 +07:00
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
tunnel = NULL;
|
|
|
|
si = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-09-08 03:12:54 +07:00
|
|
|
if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
|
2010-04-02 13:19:10 +07:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2017-04-03 17:03:13 +07:00
|
|
|
session, L2TP_CMD_SESSION_GET) < 0) {
|
|
|
|
l2tp_session_dec_refcount(session);
|
2018-04-13 01:50:33 +07:00
|
|
|
l2tp_tunnel_dec_refcount(tunnel);
|
2010-04-02 13:19:10 +07:00
|
|
|
break;
|
2017-04-03 17:03:13 +07:00
|
|
|
}
|
|
|
|
l2tp_session_dec_refcount(session);
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
si++;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
cb->args[0] = ti;
|
|
|
|
cb->args[1] = si;
|
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
}
|
|
|
|
|
2016-09-01 13:24:41 +07:00
|
|
|
static const struct nla_policy l2tp_nl_policy[L2TP_ATTR_MAX + 1] = {
|
2010-04-02 13:19:10 +07:00
|
|
|
[L2TP_ATTR_NONE] = { .type = NLA_UNSPEC, },
|
|
|
|
[L2TP_ATTR_PW_TYPE] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_ENCAP_TYPE] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_OFFSET] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_DATA_SEQ] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_L2SPEC_TYPE] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_L2SPEC_LEN] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_PROTO_VERSION] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_CONN_ID] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_PEER_CONN_ID] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_SESSION_ID] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_PEER_SESSION_ID] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_UDP_CSUM] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_VLAN_ID] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_DEBUG] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_RECV_SEQ] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_SEND_SEQ] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_LNS_MODE] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_USING_IPSEC] = { .type = NLA_U8, },
|
|
|
|
[L2TP_ATTR_RECV_TIMEOUT] = { .type = NLA_MSECS, },
|
|
|
|
[L2TP_ATTR_FD] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_IP_SADDR] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_IP_DADDR] = { .type = NLA_U32, },
|
|
|
|
[L2TP_ATTR_UDP_SPORT] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_UDP_DPORT] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_MTU] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_MRU] = { .type = NLA_U16, },
|
|
|
|
[L2TP_ATTR_STATS] = { .type = NLA_NESTED, },
|
2012-04-30 04:48:52 +07:00
|
|
|
[L2TP_ATTR_IP6_SADDR] = {
|
|
|
|
.type = NLA_BINARY,
|
|
|
|
.len = sizeof(struct in6_addr),
|
|
|
|
},
|
|
|
|
[L2TP_ATTR_IP6_DADDR] = {
|
|
|
|
.type = NLA_BINARY,
|
|
|
|
.len = sizeof(struct in6_addr),
|
|
|
|
},
|
2010-04-02 13:19:10 +07:00
|
|
|
[L2TP_ATTR_IFNAME] = {
|
|
|
|
.type = NLA_NUL_STRING,
|
|
|
|
.len = IFNAMSIZ - 1,
|
|
|
|
},
|
|
|
|
[L2TP_ATTR_COOKIE] = {
|
|
|
|
.type = NLA_BINARY,
|
|
|
|
.len = 8,
|
|
|
|
},
|
|
|
|
[L2TP_ATTR_PEER_COOKIE] = {
|
|
|
|
.type = NLA_BINARY,
|
|
|
|
.len = 8,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-11-14 23:14:46 +07:00
|
|
|
static const struct genl_ops l2tp_nl_ops[] = {
|
2010-04-02 13:19:10 +07:00
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_NOOP,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_noop,
|
|
|
|
/* can be retrieved by unprivileged users */
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_TUNNEL_CREATE,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_tunnel_create,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_TUNNEL_DELETE,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_tunnel_delete,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_TUNNEL_MODIFY,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_tunnel_modify,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_TUNNEL_GET,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_tunnel_get,
|
|
|
|
.dumpit = l2tp_nl_cmd_tunnel_dump,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_SESSION_CREATE,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_session_create,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_SESSION_DELETE,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_session_delete,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_SESSION_MODIFY,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_session_modify,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = L2TP_CMD_SESSION_GET,
|
2019-04-26 19:07:31 +07:00
|
|
|
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
|
2010-04-02 13:19:10 +07:00
|
|
|
.doit = l2tp_nl_cmd_session_get,
|
|
|
|
.dumpit = l2tp_nl_cmd_session_dump,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2016-10-24 19:40:05 +07:00
|
|
|
static struct genl_family l2tp_nl_family __ro_after_init = {
|
2016-10-24 19:40:03 +07:00
|
|
|
.name = L2TP_GENL_NAME,
|
|
|
|
.version = L2TP_GENL_VERSION,
|
|
|
|
.hdrsize = 0,
|
|
|
|
.maxattr = L2TP_ATTR_MAX,
|
genetlink: make policy common to family
Since maxattr is common, the policy can't really differ sanely,
so make it common as well.
The only user that did in fact manage to make a non-common policy
is taskstats, which has to be really careful about it (since it's
still using a common maxattr!). This is no longer supported, but
we can fake it using pre_doit.
This reduces the size of e.g. nl80211.o (which has lots of commands):
text data bss dec hex filename
398745 14323 2240 415308 6564c net/wireless/nl80211.o (before)
397913 14331 2240 414484 65314 net/wireless/nl80211.o (after)
--------------------------------
-832 +8 0 -824
Which is obviously just 8 bytes for each command, and an added 8
bytes for the new policy pointer. I'm not sure why the ops list is
counted as .text though.
Most of the code transformations were done using the following spatch:
@ops@
identifier OPS;
expression POLICY;
@@
struct genl_ops OPS[] = {
...,
{
- .policy = POLICY,
},
...
};
@@
identifier ops.OPS;
expression ops.POLICY;
identifier fam;
expression M;
@@
struct genl_family fam = {
.ops = OPS,
.maxattr = M,
+ .policy = POLICY,
...
};
This also gets rid of devlink_nl_cmd_region_read_dumpit() accessing
the cb->data as ops, which we want to change in a later genl patch.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-03-22 04:51:02 +07:00
|
|
|
.policy = l2tp_nl_policy,
|
2016-10-24 19:40:03 +07:00
|
|
|
.netnsok = true,
|
|
|
|
.module = THIS_MODULE,
|
|
|
|
.ops = l2tp_nl_ops,
|
|
|
|
.n_ops = ARRAY_SIZE(l2tp_nl_ops),
|
|
|
|
.mcgrps = l2tp_multicast_group,
|
|
|
|
.n_mcgrps = ARRAY_SIZE(l2tp_multicast_group),
|
|
|
|
};
|
|
|
|
|
2010-04-02 13:19:10 +07:00
|
|
|
int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (pw_type >= __L2TP_PWTYPE_MAX)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
genl_lock();
|
|
|
|
ret = -EBUSY;
|
|
|
|
if (l2tp_nl_cmd_ops[pw_type])
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
l2tp_nl_cmd_ops[pw_type] = ops;
|
2011-04-18 07:01:05 +07:00
|
|
|
ret = 0;
|
2010-04-02 13:19:10 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
genl_unlock();
|
|
|
|
err:
|
2011-04-18 07:01:05 +07:00
|
|
|
return ret;
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(l2tp_nl_register_ops);
|
|
|
|
|
|
|
|
void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type)
|
|
|
|
{
|
|
|
|
if (pw_type < __L2TP_PWTYPE_MAX) {
|
|
|
|
genl_lock();
|
|
|
|
l2tp_nl_cmd_ops[pw_type] = NULL;
|
|
|
|
genl_unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(l2tp_nl_unregister_ops);
|
|
|
|
|
2016-10-24 19:40:05 +07:00
|
|
|
static int __init l2tp_nl_init(void)
|
2010-04-02 13:19:10 +07:00
|
|
|
{
|
2012-05-16 16:55:56 +07:00
|
|
|
pr_info("L2TP netlink interface\n");
|
2016-10-24 19:40:03 +07:00
|
|
|
return genl_register_family(&l2tp_nl_family);
|
2010-04-02 13:19:10 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2tp_nl_cleanup(void)
|
|
|
|
{
|
|
|
|
genl_unregister_family(&l2tp_nl_family);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(l2tp_nl_init);
|
|
|
|
module_exit(l2tp_nl_cleanup);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
|
|
|
|
MODULE_DESCRIPTION("L2TP netlink");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION("1.0");
|
2012-05-29 16:30:41 +07:00
|
|
|
MODULE_ALIAS_GENL_FAMILY("l2tp");
|