mirror of https://gitee.com/openkylin/linux.git
inet_diag: support for wider protocol numbers
After commit bf9765145b
("sock: Make sk_protocol a 16-bit value")
the current size of 'sdiag_protocol' is not sufficient to represent
the possible protocol values.
This change introduces a new inet diag request attribute to let
user space specify the relevant protocol number using u32 values.
The attribute is parsed by inet diag core on get/dump command
and the extended protocol value, if available, is preferred to
'sdiag_protocol' to lookup the diag handler.
The parse attributed are exposed to all the diag handlers via
the cb->data.
Note that inet_diag_dump_one_icsk() is left unmodified, as it
will not be used by protocol using the extended attribute.
Suggested-by: David S. Miller <davem@davemloft.net>
Co-developed-by: Christoph Paasch <cpaasch@apple.com>
Signed-off-by: Christoph Paasch <cpaasch@apple.com>
Acked-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5ca670e58d
commit
3f935c75eb
|
@ -65,6 +65,7 @@ enum {
|
||||||
INET_DIAG_REQ_NONE,
|
INET_DIAG_REQ_NONE,
|
||||||
INET_DIAG_REQ_BYTECODE,
|
INET_DIAG_REQ_BYTECODE,
|
||||||
INET_DIAG_REQ_SK_BPF_STORAGES,
|
INET_DIAG_REQ_SK_BPF_STORAGES,
|
||||||
|
INET_DIAG_REQ_PROTOCOL,
|
||||||
__INET_DIAG_REQ_MAX,
|
__INET_DIAG_REQ_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3566,6 +3566,7 @@ int sock_load_diag_module(int family, int protocol)
|
||||||
#ifdef CONFIG_INET
|
#ifdef CONFIG_INET
|
||||||
if (family == AF_INET &&
|
if (family == AF_INET &&
|
||||||
protocol != IPPROTO_RAW &&
|
protocol != IPPROTO_RAW &&
|
||||||
|
protocol < MAX_INET_PROTOS &&
|
||||||
!rcu_access_pointer(inet_protos[protocol]))
|
!rcu_access_pointer(inet_protos[protocol]))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -52,6 +52,11 @@ static DEFINE_MUTEX(inet_diag_table_mutex);
|
||||||
|
|
||||||
static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
|
static const struct inet_diag_handler *inet_diag_lock_handler(int proto)
|
||||||
{
|
{
|
||||||
|
if (proto < 0 || proto >= IPPROTO_MAX) {
|
||||||
|
mutex_lock(&inet_diag_table_mutex);
|
||||||
|
return ERR_PTR(-ENOENT);
|
||||||
|
}
|
||||||
|
|
||||||
if (!inet_diag_table[proto])
|
if (!inet_diag_table[proto])
|
||||||
sock_load_diag_module(AF_INET, proto);
|
sock_load_diag_module(AF_INET, proto);
|
||||||
|
|
||||||
|
@ -181,6 +186,28 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill);
|
EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill);
|
||||||
|
|
||||||
|
static void inet_diag_parse_attrs(const struct nlmsghdr *nlh, int hdrlen,
|
||||||
|
struct nlattr **req_nlas)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
int remaining;
|
||||||
|
|
||||||
|
nlmsg_for_each_attr(nla, nlh, hdrlen, remaining) {
|
||||||
|
int type = nla_type(nla);
|
||||||
|
|
||||||
|
if (type < __INET_DIAG_REQ_MAX)
|
||||||
|
req_nlas[type] = nla;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inet_diag_get_protocol(const struct inet_diag_req_v2 *req,
|
||||||
|
const struct inet_diag_dump_data *data)
|
||||||
|
{
|
||||||
|
if (data->req_nlas[INET_DIAG_REQ_PROTOCOL])
|
||||||
|
return nla_get_u32(data->req_nlas[INET_DIAG_REQ_PROTOCOL]);
|
||||||
|
return req->sdiag_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
#define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
#define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
|
||||||
|
|
||||||
int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
|
int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
|
||||||
|
@ -198,7 +225,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
|
||||||
void *info = NULL;
|
void *info = NULL;
|
||||||
|
|
||||||
cb_data = cb->data;
|
cb_data = cb->data;
|
||||||
handler = inet_diag_table[req->sdiag_protocol];
|
handler = inet_diag_table[inet_diag_get_protocol(req, cb_data)];
|
||||||
BUG_ON(!handler);
|
BUG_ON(!handler);
|
||||||
|
|
||||||
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
|
||||||
|
@ -539,20 +566,25 @@ EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
|
||||||
|
|
||||||
static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
|
static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
|
||||||
const struct nlmsghdr *nlh,
|
const struct nlmsghdr *nlh,
|
||||||
|
int hdrlen,
|
||||||
const struct inet_diag_req_v2 *req)
|
const struct inet_diag_req_v2 *req)
|
||||||
{
|
{
|
||||||
const struct inet_diag_handler *handler;
|
const struct inet_diag_handler *handler;
|
||||||
int err;
|
struct inet_diag_dump_data dump_data;
|
||||||
|
int err, protocol;
|
||||||
|
|
||||||
handler = inet_diag_lock_handler(req->sdiag_protocol);
|
memset(&dump_data, 0, sizeof(dump_data));
|
||||||
|
inet_diag_parse_attrs(nlh, hdrlen, dump_data.req_nlas);
|
||||||
|
protocol = inet_diag_get_protocol(req, &dump_data);
|
||||||
|
|
||||||
|
handler = inet_diag_lock_handler(protocol);
|
||||||
if (IS_ERR(handler)) {
|
if (IS_ERR(handler)) {
|
||||||
err = PTR_ERR(handler);
|
err = PTR_ERR(handler);
|
||||||
} else if (cmd == SOCK_DIAG_BY_FAMILY) {
|
} else if (cmd == SOCK_DIAG_BY_FAMILY) {
|
||||||
struct inet_diag_dump_data empty_dump_data = {};
|
|
||||||
struct netlink_callback cb = {
|
struct netlink_callback cb = {
|
||||||
.nlh = nlh,
|
.nlh = nlh,
|
||||||
.skb = in_skb,
|
.skb = in_skb,
|
||||||
.data = &empty_dump_data,
|
.data = &dump_data,
|
||||||
};
|
};
|
||||||
err = handler->dump_one(&cb, req);
|
err = handler->dump_one(&cb, req);
|
||||||
} else if (cmd == SOCK_DESTROY && handler->destroy) {
|
} else if (cmd == SOCK_DESTROY && handler->destroy) {
|
||||||
|
@ -1103,13 +1135,16 @@ EXPORT_SYMBOL_GPL(inet_diag_dump_icsk);
|
||||||
static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
|
static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
|
||||||
const struct inet_diag_req_v2 *r)
|
const struct inet_diag_req_v2 *r)
|
||||||
{
|
{
|
||||||
|
struct inet_diag_dump_data *cb_data = cb->data;
|
||||||
const struct inet_diag_handler *handler;
|
const struct inet_diag_handler *handler;
|
||||||
u32 prev_min_dump_alloc;
|
u32 prev_min_dump_alloc;
|
||||||
int err = 0;
|
int protocol, err = 0;
|
||||||
|
|
||||||
|
protocol = inet_diag_get_protocol(r, cb_data);
|
||||||
|
|
||||||
again:
|
again:
|
||||||
prev_min_dump_alloc = cb->min_dump_alloc;
|
prev_min_dump_alloc = cb->min_dump_alloc;
|
||||||
handler = inet_diag_lock_handler(r->sdiag_protocol);
|
handler = inet_diag_lock_handler(protocol);
|
||||||
if (!IS_ERR(handler))
|
if (!IS_ERR(handler))
|
||||||
handler->dump(skb, cb, r);
|
handler->dump(skb, cb, r);
|
||||||
else
|
else
|
||||||
|
@ -1139,19 +1174,13 @@ static int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen)
|
||||||
struct inet_diag_dump_data *cb_data;
|
struct inet_diag_dump_data *cb_data;
|
||||||
struct sk_buff *skb = cb->skb;
|
struct sk_buff *skb = cb->skb;
|
||||||
struct nlattr *nla;
|
struct nlattr *nla;
|
||||||
int rem, err;
|
int err;
|
||||||
|
|
||||||
cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL);
|
cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL);
|
||||||
if (!cb_data)
|
if (!cb_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
nla_for_each_attr(nla, nlmsg_attrdata(nlh, hdrlen),
|
inet_diag_parse_attrs(nlh, hdrlen, cb_data->req_nlas);
|
||||||
nlmsg_attrlen(nlh, hdrlen), rem) {
|
|
||||||
int type = nla_type(nla);
|
|
||||||
|
|
||||||
if (type < __INET_DIAG_REQ_MAX)
|
|
||||||
cb_data->req_nlas[type] = nla;
|
|
||||||
}
|
|
||||||
|
|
||||||
nla = cb_data->inet_diag_nla_bc;
|
nla = cb_data->inet_diag_nla_bc;
|
||||||
if (nla) {
|
if (nla) {
|
||||||
|
@ -1237,7 +1266,8 @@ static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
|
||||||
req.idiag_states = rc->idiag_states;
|
req.idiag_states = rc->idiag_states;
|
||||||
req.id = rc->id;
|
req.id = rc->id;
|
||||||
|
|
||||||
return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, &req);
|
return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh,
|
||||||
|
sizeof(struct inet_diag_req), &req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
|
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||||
|
@ -1279,7 +1309,8 @@ static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
|
||||||
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
|
return netlink_dump_start(net->diag_nlsk, skb, h, &c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h));
|
return inet_diag_cmd_exact(h->nlmsg_type, skb, h, hdrlen,
|
||||||
|
nlmsg_data(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
|
|
Loading…
Reference in New Issue