net/ncsi: Extend NC-SI Netlink interface to allow user space to send NC-SI command
The new command (NCSI_CMD_SEND_CMD) is added to allow user space application to send NC-SI command to the network card. Also, add a new attribute (NCSI_ATTR_DATA) for transferring request and response. The work flow is as below. Request: User space application -> Netlink interface (msg) -> new Netlink handler - ncsi_send_cmd_nl() -> ncsi_xmit_cmd() Response: Response received - ncsi_rcv_rsp() -> internal response handler - ncsi_rsp_handler_xxx() -> ncsi_rsp_handler_netlink() -> ncsi_send_netlink_rsp () -> Netlink interface (msg) -> user space application Command timeout - ncsi_request_timeout() -> ncsi_send_netlink_timeout () -> Netlink interface (msg with zero data length) -> user space application Error: Error detected -> ncsi_send_netlink_err () -> Netlink interface (err msg) -> user space application Signed-off-by: Justin Lee <justin.lee1@dell.com> Reviewed-by: Samuel Mendoza-Jonas <sam@mendozajonas.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
6384e48323
commit
9771b8ccdf
|
@ -23,6 +23,9 @@
|
||||||
* optionally the preferred NCSI_ATTR_CHANNEL_ID.
|
* optionally the preferred NCSI_ATTR_CHANNEL_ID.
|
||||||
* @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination.
|
* @NCSI_CMD_CLEAR_INTERFACE: clear any preferred package/channel combination.
|
||||||
* Requires NCSI_ATTR_IFINDEX.
|
* Requires NCSI_ATTR_IFINDEX.
|
||||||
|
* @NCSI_CMD_SEND_CMD: send NC-SI command to network card.
|
||||||
|
* Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID
|
||||||
|
* and NCSI_ATTR_CHANNEL_ID.
|
||||||
* @NCSI_CMD_MAX: highest command number
|
* @NCSI_CMD_MAX: highest command number
|
||||||
*/
|
*/
|
||||||
enum ncsi_nl_commands {
|
enum ncsi_nl_commands {
|
||||||
|
@ -30,6 +33,7 @@ enum ncsi_nl_commands {
|
||||||
NCSI_CMD_PKG_INFO,
|
NCSI_CMD_PKG_INFO,
|
||||||
NCSI_CMD_SET_INTERFACE,
|
NCSI_CMD_SET_INTERFACE,
|
||||||
NCSI_CMD_CLEAR_INTERFACE,
|
NCSI_CMD_CLEAR_INTERFACE,
|
||||||
|
NCSI_CMD_SEND_CMD,
|
||||||
|
|
||||||
__NCSI_CMD_AFTER_LAST,
|
__NCSI_CMD_AFTER_LAST,
|
||||||
NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
|
NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1
|
||||||
|
@ -43,6 +47,7 @@ enum ncsi_nl_commands {
|
||||||
* @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes
|
* @NCSI_ATTR_PACKAGE_LIST: nested array of NCSI_PKG_ATTR attributes
|
||||||
* @NCSI_ATTR_PACKAGE_ID: package ID
|
* @NCSI_ATTR_PACKAGE_ID: package ID
|
||||||
* @NCSI_ATTR_CHANNEL_ID: channel ID
|
* @NCSI_ATTR_CHANNEL_ID: channel ID
|
||||||
|
* @NCSI_ATTR_DATA: command payload
|
||||||
* @NCSI_ATTR_MAX: highest attribute number
|
* @NCSI_ATTR_MAX: highest attribute number
|
||||||
*/
|
*/
|
||||||
enum ncsi_nl_attrs {
|
enum ncsi_nl_attrs {
|
||||||
|
@ -51,6 +56,7 @@ enum ncsi_nl_attrs {
|
||||||
NCSI_ATTR_PACKAGE_LIST,
|
NCSI_ATTR_PACKAGE_LIST,
|
||||||
NCSI_ATTR_PACKAGE_ID,
|
NCSI_ATTR_PACKAGE_ID,
|
||||||
NCSI_ATTR_CHANNEL_ID,
|
NCSI_ATTR_CHANNEL_ID,
|
||||||
|
NCSI_ATTR_DATA,
|
||||||
|
|
||||||
__NCSI_ATTR_AFTER_LAST,
|
__NCSI_ATTR_AFTER_LAST,
|
||||||
NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
|
NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1
|
||||||
|
|
|
@ -175,6 +175,8 @@ struct ncsi_package;
|
||||||
#define NCSI_RESERVED_CHANNEL 0x1f
|
#define NCSI_RESERVED_CHANNEL 0x1f
|
||||||
#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
|
#define NCSI_CHANNEL_INDEX(c) ((c) & ((1 << NCSI_PACKAGE_SHIFT) - 1))
|
||||||
#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
|
#define NCSI_TO_CHANNEL(p, c) (((p) << NCSI_PACKAGE_SHIFT) | (c))
|
||||||
|
#define NCSI_MAX_PACKAGE 8
|
||||||
|
#define NCSI_MAX_CHANNEL 32
|
||||||
|
|
||||||
struct ncsi_channel {
|
struct ncsi_channel {
|
||||||
unsigned char id;
|
unsigned char id;
|
||||||
|
@ -220,11 +222,15 @@ struct ncsi_request {
|
||||||
bool used; /* Request that has been assigned */
|
bool used; /* Request that has been assigned */
|
||||||
unsigned int flags; /* NCSI request property */
|
unsigned int flags; /* NCSI request property */
|
||||||
#define NCSI_REQ_FLAG_EVENT_DRIVEN 1
|
#define NCSI_REQ_FLAG_EVENT_DRIVEN 1
|
||||||
|
#define NCSI_REQ_FLAG_NETLINK_DRIVEN 2
|
||||||
struct ncsi_dev_priv *ndp; /* Associated NCSI device */
|
struct ncsi_dev_priv *ndp; /* Associated NCSI device */
|
||||||
struct sk_buff *cmd; /* Associated NCSI command packet */
|
struct sk_buff *cmd; /* Associated NCSI command packet */
|
||||||
struct sk_buff *rsp; /* Associated NCSI response packet */
|
struct sk_buff *rsp; /* Associated NCSI response packet */
|
||||||
struct timer_list timer; /* Timer on waiting for response */
|
struct timer_list timer; /* Timer on waiting for response */
|
||||||
bool enabled; /* Time has been enabled or not */
|
bool enabled; /* Time has been enabled or not */
|
||||||
|
u32 snd_seq; /* netlink sending sequence number */
|
||||||
|
u32 snd_portid; /* netlink portid of sender */
|
||||||
|
struct nlmsghdr nlhdr; /* netlink message header */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -310,6 +316,7 @@ struct ncsi_cmd_arg {
|
||||||
unsigned int dwords[4];
|
unsigned int dwords[4];
|
||||||
};
|
};
|
||||||
unsigned char *data; /* NCSI OEM data */
|
unsigned char *data; /* NCSI OEM data */
|
||||||
|
struct genl_info *info; /* Netlink information */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct list_head ncsi_dev_list;
|
extern struct list_head ncsi_dev_list;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <net/ncsi.h>
|
#include <net/ncsi.h>
|
||||||
#include <net/net_namespace.h>
|
#include <net/net_namespace.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
#include <net/genetlink.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "ncsi-pkt.h"
|
#include "ncsi-pkt.h"
|
||||||
|
@ -346,6 +347,13 @@ int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
|
||||||
if (!nr)
|
if (!nr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* track netlink information */
|
||||||
|
if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
||||||
|
nr->snd_seq = nca->info->snd_seq;
|
||||||
|
nr->snd_portid = nca->info->snd_portid;
|
||||||
|
nr->nlhdr = *nca->info->nlhdr;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare the packet */
|
/* Prepare the packet */
|
||||||
nca->id = nr->id;
|
nca->id = nr->id;
|
||||||
ret = nch->handler(nr->cmd, nca);
|
ret = nch->handler(nr->cmd, nca);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <net/addrconf.h>
|
#include <net/addrconf.h>
|
||||||
#include <net/ipv6.h>
|
#include <net/ipv6.h>
|
||||||
#include <net/if_inet6.h>
|
#include <net/if_inet6.h>
|
||||||
|
#include <net/genetlink.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "ncsi-pkt.h"
|
#include "ncsi-pkt.h"
|
||||||
|
@ -406,6 +407,9 @@ static void ncsi_request_timeout(struct timer_list *t)
|
||||||
{
|
{
|
||||||
struct ncsi_request *nr = from_timer(nr, t, timer);
|
struct ncsi_request *nr = from_timer(nr, t, timer);
|
||||||
struct ncsi_dev_priv *ndp = nr->ndp;
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
||||||
|
struct ncsi_cmd_pkt *cmd;
|
||||||
|
struct ncsi_package *np;
|
||||||
|
struct ncsi_channel *nc;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
/* If the request already had associated response,
|
/* If the request already had associated response,
|
||||||
|
@ -419,6 +423,18 @@ static void ncsi_request_timeout(struct timer_list *t)
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ndp->lock, flags);
|
spin_unlock_irqrestore(&ndp->lock, flags);
|
||||||
|
|
||||||
|
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
||||||
|
if (nr->cmd) {
|
||||||
|
/* Find the package */
|
||||||
|
cmd = (struct ncsi_cmd_pkt *)
|
||||||
|
skb_network_header(nr->cmd);
|
||||||
|
ncsi_find_package_and_channel(ndp,
|
||||||
|
cmd->cmd.common.channel,
|
||||||
|
&np, &nc);
|
||||||
|
ncsi_send_netlink_timeout(nr, np, nc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Release the request */
|
/* Release the request */
|
||||||
ncsi_free_request(nr);
|
ncsi_free_request(nr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <uapi/linux/ncsi.h>
|
#include <uapi/linux/ncsi.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include "ncsi-pkt.h"
|
||||||
#include "ncsi-netlink.h"
|
#include "ncsi-netlink.h"
|
||||||
|
|
||||||
static struct genl_family ncsi_genl_family;
|
static struct genl_family ncsi_genl_family;
|
||||||
|
@ -28,6 +29,7 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
|
||||||
[NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED },
|
[NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED },
|
||||||
[NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
|
[NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 },
|
||||||
[NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
|
[NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 },
|
||||||
|
[NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
|
static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
|
||||||
|
@ -365,6 +367,202 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
|
||||||
|
{
|
||||||
|
struct ncsi_dev_priv *ndp;
|
||||||
|
struct ncsi_pkt_hdr *hdr;
|
||||||
|
struct ncsi_cmd_arg nca;
|
||||||
|
unsigned char *data;
|
||||||
|
u32 package_id;
|
||||||
|
u32 channel_id;
|
||||||
|
int len, ret;
|
||||||
|
|
||||||
|
if (!info || !info->attrs) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->attrs[NCSI_ATTR_IFINDEX]) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info->attrs[NCSI_ATTR_DATA]) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
|
||||||
|
nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
|
||||||
|
if (!ndp) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
|
||||||
|
channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
|
||||||
|
|
||||||
|
if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) {
|
||||||
|
ret = -ERANGE;
|
||||||
|
goto out_netlink;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = nla_len(info->attrs[NCSI_ATTR_DATA]);
|
||||||
|
if (len < sizeof(struct ncsi_pkt_hdr)) {
|
||||||
|
netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n",
|
||||||
|
package_id);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_netlink;
|
||||||
|
} else {
|
||||||
|
data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]);
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = (struct ncsi_pkt_hdr *)data;
|
||||||
|
|
||||||
|
nca.ndp = ndp;
|
||||||
|
nca.package = (unsigned char)package_id;
|
||||||
|
nca.channel = (unsigned char)channel_id;
|
||||||
|
nca.type = hdr->type;
|
||||||
|
nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
|
||||||
|
nca.info = info;
|
||||||
|
nca.payload = ntohs(hdr->length);
|
||||||
|
nca.data = data + sizeof(*hdr);
|
||||||
|
|
||||||
|
ret = ncsi_xmit_cmd(&nca);
|
||||||
|
out_netlink:
|
||||||
|
if (ret != 0) {
|
||||||
|
netdev_err(ndp->ndev.dev,
|
||||||
|
"NCSI: Error %d sending command\n",
|
||||||
|
ret);
|
||||||
|
ncsi_send_netlink_err(ndp->ndev.dev,
|
||||||
|
info->snd_seq,
|
||||||
|
info->snd_portid,
|
||||||
|
info->nlhdr,
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncsi_send_netlink_rsp(struct ncsi_request *nr,
|
||||||
|
struct ncsi_package *np,
|
||||||
|
struct ncsi_channel *nc)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct net *net;
|
||||||
|
void *hdr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
net = dev_net(nr->rsp->dev);
|
||||||
|
|
||||||
|
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
|
||||||
|
&ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
|
||||||
|
if (!hdr) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex);
|
||||||
|
if (np)
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
|
||||||
|
if (nc)
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
|
||||||
|
else
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
|
||||||
|
|
||||||
|
rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data);
|
||||||
|
if (rc)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
genlmsg_end(skb, hdr);
|
||||||
|
return genlmsg_unicast(net, skb, nr->snd_portid);
|
||||||
|
|
||||||
|
err:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncsi_send_netlink_timeout(struct ncsi_request *nr,
|
||||||
|
struct ncsi_package *np,
|
||||||
|
struct ncsi_channel *nc)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct net *net;
|
||||||
|
void *hdr;
|
||||||
|
|
||||||
|
skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
|
||||||
|
&ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
|
||||||
|
if (!hdr) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
net = dev_net(nr->cmd->dev);
|
||||||
|
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex);
|
||||||
|
|
||||||
|
if (np)
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
|
||||||
|
else
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID,
|
||||||
|
NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *)
|
||||||
|
nr->cmd->data)->channel)));
|
||||||
|
|
||||||
|
if (nc)
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
|
||||||
|
else
|
||||||
|
nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
|
||||||
|
|
||||||
|
genlmsg_end(skb, hdr);
|
||||||
|
return genlmsg_unicast(net, skb, nr->snd_portid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncsi_send_netlink_err(struct net_device *dev,
|
||||||
|
u32 snd_seq,
|
||||||
|
u32 snd_portid,
|
||||||
|
struct nlmsghdr *nlhdr,
|
||||||
|
int err)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct nlmsgerr *nle;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct net *net;
|
||||||
|
|
||||||
|
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
net = dev_net(dev);
|
||||||
|
|
||||||
|
nlh = nlmsg_put(skb, snd_portid, snd_seq,
|
||||||
|
NLMSG_ERROR, sizeof(*nle), 0);
|
||||||
|
nle = (struct nlmsgerr *)nlmsg_data(nlh);
|
||||||
|
nle->error = err;
|
||||||
|
memcpy(&nle->msg, nlhdr, sizeof(*nlh));
|
||||||
|
|
||||||
|
nlmsg_end(skb, nlh);
|
||||||
|
|
||||||
|
return nlmsg_unicast(net->genl_sock, skb, snd_portid);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct genl_ops ncsi_ops[] = {
|
static const struct genl_ops ncsi_ops[] = {
|
||||||
{
|
{
|
||||||
.cmd = NCSI_CMD_PKG_INFO,
|
.cmd = NCSI_CMD_PKG_INFO,
|
||||||
|
@ -385,6 +583,12 @@ static const struct genl_ops ncsi_ops[] = {
|
||||||
.doit = ncsi_clear_interface_nl,
|
.doit = ncsi_clear_interface_nl,
|
||||||
.flags = GENL_ADMIN_PERM,
|
.flags = GENL_ADMIN_PERM,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.cmd = NCSI_CMD_SEND_CMD,
|
||||||
|
.policy = ncsi_genl_policy,
|
||||||
|
.doit = ncsi_send_cmd_nl,
|
||||||
|
.flags = GENL_ADMIN_PERM,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct genl_family ncsi_genl_family __ro_after_init = {
|
static struct genl_family ncsi_genl_family __ro_after_init = {
|
||||||
|
|
|
@ -14,6 +14,18 @@
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
int ncsi_send_netlink_rsp(struct ncsi_request *nr,
|
||||||
|
struct ncsi_package *np,
|
||||||
|
struct ncsi_channel *nc);
|
||||||
|
int ncsi_send_netlink_timeout(struct ncsi_request *nr,
|
||||||
|
struct ncsi_package *np,
|
||||||
|
struct ncsi_channel *nc);
|
||||||
|
int ncsi_send_netlink_err(struct net_device *dev,
|
||||||
|
u32 snd_seq,
|
||||||
|
u32 snd_portid,
|
||||||
|
struct nlmsghdr *nlhdr,
|
||||||
|
int err);
|
||||||
|
|
||||||
int ncsi_init_netlink(struct net_device *dev);
|
int ncsi_init_netlink(struct net_device *dev);
|
||||||
int ncsi_unregister_netlink(struct net_device *dev);
|
int ncsi_unregister_netlink(struct net_device *dev);
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
#include <net/ncsi.h>
|
#include <net/ncsi.h>
|
||||||
#include <net/net_namespace.h>
|
#include <net/net_namespace.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
#include <net/genetlink.h>
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "ncsi-pkt.h"
|
#include "ncsi-pkt.h"
|
||||||
|
#include "ncsi-netlink.h"
|
||||||
|
|
||||||
static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
|
static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
|
||||||
unsigned short payload)
|
unsigned short payload)
|
||||||
|
@ -32,15 +34,25 @@ static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
|
||||||
* before calling this function.
|
* before calling this function.
|
||||||
*/
|
*/
|
||||||
h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
|
h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
|
||||||
if (h->common.revision != NCSI_PKT_REVISION)
|
|
||||||
|
if (h->common.revision != NCSI_PKT_REVISION) {
|
||||||
|
netdev_dbg(nr->ndp->ndev.dev,
|
||||||
|
"NCSI: unsupported header revision\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (ntohs(h->common.length) != payload)
|
}
|
||||||
|
if (ntohs(h->common.length) != payload) {
|
||||||
|
netdev_dbg(nr->ndp->ndev.dev,
|
||||||
|
"NCSI: payload length mismatched\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check on code and reason */
|
/* Check on code and reason */
|
||||||
if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
|
if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
|
||||||
ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
|
ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR) {
|
||||||
return -EINVAL;
|
netdev_dbg(nr->ndp->ndev.dev,
|
||||||
|
"NCSI: non zero response/reason code\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
/* Validate checksum, which might be zeroes if the
|
/* Validate checksum, which might be zeroes if the
|
||||||
* sender doesn't support checksum according to NCSI
|
* sender doesn't support checksum according to NCSI
|
||||||
|
@ -52,8 +64,11 @@ static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
|
||||||
|
|
||||||
checksum = ncsi_calculate_checksum((unsigned char *)h,
|
checksum = ncsi_calculate_checksum((unsigned char *)h,
|
||||||
sizeof(*h) + payload - 4);
|
sizeof(*h) + payload - 4);
|
||||||
if (*pchecksum != htonl(checksum))
|
|
||||||
|
if (*pchecksum != htonl(checksum)) {
|
||||||
|
netdev_dbg(nr->ndp->ndev.dev, "NCSI: checksum mismatched\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -941,6 +956,26 @@ static int ncsi_rsp_handler_gpuuid(struct ncsi_request *nr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ncsi_rsp_handler_netlink(struct ncsi_request *nr)
|
||||||
|
{
|
||||||
|
struct ncsi_dev_priv *ndp = nr->ndp;
|
||||||
|
struct ncsi_rsp_pkt *rsp;
|
||||||
|
struct ncsi_package *np;
|
||||||
|
struct ncsi_channel *nc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Find the package */
|
||||||
|
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
|
||||||
|
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
|
||||||
|
&np, &nc);
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = ncsi_send_netlink_rsp(nr, np, nc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static struct ncsi_rsp_handler {
|
static struct ncsi_rsp_handler {
|
||||||
unsigned char type;
|
unsigned char type;
|
||||||
int payload;
|
int payload;
|
||||||
|
@ -1043,6 +1078,17 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
|
||||||
netdev_warn(ndp->ndev.dev,
|
netdev_warn(ndp->ndev.dev,
|
||||||
"NCSI: 'bad' packet ignored for type 0x%x\n",
|
"NCSI: 'bad' packet ignored for type 0x%x\n",
|
||||||
hdr->type);
|
hdr->type);
|
||||||
|
|
||||||
|
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
||||||
|
if (ret == -EPERM)
|
||||||
|
goto out_netlink;
|
||||||
|
else
|
||||||
|
ncsi_send_netlink_err(ndp->ndev.dev,
|
||||||
|
nr->snd_seq,
|
||||||
|
nr->snd_portid,
|
||||||
|
&nr->nlhdr,
|
||||||
|
ret);
|
||||||
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1052,6 +1098,17 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
|
||||||
netdev_err(ndp->ndev.dev,
|
netdev_err(ndp->ndev.dev,
|
||||||
"NCSI: Handler for packet type 0x%x returned %d\n",
|
"NCSI: Handler for packet type 0x%x returned %d\n",
|
||||||
hdr->type, ret);
|
hdr->type, ret);
|
||||||
|
|
||||||
|
out_netlink:
|
||||||
|
if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
|
||||||
|
ret = ncsi_rsp_handler_netlink(nr);
|
||||||
|
if (ret) {
|
||||||
|
netdev_err(ndp->ndev.dev,
|
||||||
|
"NCSI: Netlink handler for packet type 0x%x returned %d\n",
|
||||||
|
hdr->type, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
ncsi_free_request(nr);
|
ncsi_free_request(nr);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue