Commit a3c4b484 authored by Jiri Pirko's avatar Jiri Pirko Committed by Stephen Hemminger

add devlink tool

Add new tool called devlink which is userspace counterpart of devlink
Netlink socket.
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
parent 4952b459
......@@ -41,7 +41,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2
CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS)
YACCFLAGS = -d -t -v
SUBDIRS=lib ip tc bridge misc netem genl tipc man
SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man
LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
LDLIBS += $(LIBNETLINK)
......
include ../Config
ifeq ($(HAVE_MNL),y)
DEVLINKOBJ = devlink.o mnlg.o
TARGETS=devlink
CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags)
LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs)
endif
all: $(TARGETS) $(LIBS)
devlink: $(DEVLINKOBJ)
install: all
install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR)
clean:
rm -f $(DEVLINKOBJ) $(TARGETS)
This diff is collapsed.
/*
* mnlg.c Generic Netlink helpers for libmnl
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Jiri Pirko <jiri@mellanox.com>
*/
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include "mnlg.h"
struct mnlg_socket {
struct mnl_socket *nl;
char *buf;
uint32_t id;
uint8_t version;
unsigned int seq;
unsigned int portid;
};
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
uint16_t flags, uint32_t id,
uint8_t version)
{
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
nlh = mnl_nlmsg_put_header(nlg->buf);
nlh->nlmsg_type = id;
nlh->nlmsg_flags = flags;
nlg->seq = time(NULL);
nlh->nlmsg_seq = nlg->seq;
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = cmd;
genl->version = version;
return nlh;
}
struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
uint16_t flags)
{
return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
}
int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
{
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
}
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
{
int err;
do {
err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
MNL_SOCKET_BUFFER_SIZE);
if (err <= 0)
break;
err = mnl_cb_run(nlg->buf, err, nlg->seq, nlg->portid,
data_cb, data);
} while (err > 0);
return err;
}
struct group_info {
bool found;
uint32_t id;
const char *name;
};
static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0)
return MNL_CB_OK;
switch (type) {
case CTRL_ATTR_MCAST_GRP_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
break;
case CTRL_ATTR_MCAST_GRP_NAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
return MNL_CB_ERROR;
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static void parse_genl_mc_grps(struct nlattr *nested,
struct group_info *group_info)
{
struct nlattr *pos;
const char *name;
mnl_attr_for_each_nested(pos, nested) {
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1] = {};
mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
if (!tb[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb[CTRL_ATTR_MCAST_GRP_ID])
continue;
name = mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]);
if (strcmp(name, group_info->name) != 0)
continue;
group_info->id = mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
group_info->found = true;
}
}
static int get_group_id_attr_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_ERROR;
if (type == CTRL_ATTR_MCAST_GROUPS &&
mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
return MNL_CB_ERROR;
tb[type] = attr;
return MNL_CB_OK;
}
static int get_group_id_cb(const struct nlmsghdr *nlh, void *data)
{
struct group_info *group_info = data;
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
mnl_attr_parse(nlh, sizeof(*genl), get_group_id_attr_cb, tb);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return MNL_CB_ERROR;
parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS], group_info);
return MNL_CB_OK;
}
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
{
struct nlmsghdr *nlh;
struct group_info group_info;
int err;
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, nlg->id);
err = mnlg_socket_send(nlg, nlh);
if (err < 0)
return err;
group_info.found = false;
group_info.name = group_name;
err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info);
if (err < 0)
return err;
if (!group_info.found) {
errno = ENOENT;
return -1;
}
err = mnl_socket_setsockopt(nlg->nl, NETLINK_ADD_MEMBERSHIP,
&group_info.id, sizeof(group_info.id));
if (err < 0)
return err;
return 0;
}
static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_ERROR;
if (type == CTRL_ATTR_FAMILY_ID &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
tb[type] = attr;
return MNL_CB_OK;
}
static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
{
uint32_t *p_id = data;
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
if (!tb[CTRL_ATTR_FAMILY_ID])
return MNL_CB_ERROR;
*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
return MNL_CB_OK;
}
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
{
struct mnlg_socket *nlg;
struct nlmsghdr *nlh;
int err;
nlg = malloc(sizeof(*nlg));
if (!nlg)
return NULL;
nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
if (!nlg->buf)
goto err_buf_alloc;
nlg->nl = mnl_socket_open(NETLINK_GENERIC);
if (!nlg->nl)
goto err_mnl_socket_open;
err = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID);
if (err < 0)
goto err_mnl_socket_bind;
nlg->portid = mnl_socket_get_portid(nlg->nl);
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
err = mnlg_socket_send(nlg, nlh);
if (err < 0)
goto err_mnlg_socket_send;
err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id);
if (err < 0)
goto err_mnlg_socket_recv_run;
nlg->version = version;
return nlg;
err_mnlg_socket_recv_run:
err_mnlg_socket_send:
err_mnl_socket_bind:
mnl_socket_close(nlg->nl);
err_mnl_socket_open:
free(nlg->buf);
err_buf_alloc:
free(nlg);
return NULL;
}
void mnlg_socket_close(struct mnlg_socket *nlg)
{
mnl_socket_close(nlg->nl);
free(nlg->buf);
free(nlg);
}
/*
* mnlg.h Generic Netlink helpers for libmnl
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Jiri Pirko <jiri@mellanox.com>
*/
#ifndef _MNLG_H_
#define _MNLG_H_
#include <libmnl/libmnl.h>
struct mnlg_socket;
struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
uint16_t flags);
int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh);
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
void mnlg_socket_close(struct mnlg_socket *nlg);
#endif /* _MNLG_H_ */
/*
* include/uapi/linux/devlink.h - Network physical device Netlink interface
* Copyright (c) 2016 Mellanox Technologies. All rights reserved.
* Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _UAPI_LINUX_DEVLINK_H_
#define _UAPI_LINUX_DEVLINK_H_
#define DEVLINK_GENL_NAME "devlink"
#define DEVLINK_GENL_VERSION 0x1
#define DEVLINK_GENL_MCGRP_CONFIG_NAME "config"
enum devlink_command {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_CMD_UNSPEC,
DEVLINK_CMD_GET, /* can dump */
DEVLINK_CMD_SET,
DEVLINK_CMD_NEW,
DEVLINK_CMD_DEL,
DEVLINK_CMD_PORT_GET, /* can dump */
DEVLINK_CMD_PORT_SET,
DEVLINK_CMD_PORT_NEW,
DEVLINK_CMD_PORT_DEL,
DEVLINK_CMD_PORT_SPLIT,
DEVLINK_CMD_PORT_UNSPLIT,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
};
enum devlink_port_type {
DEVLINK_PORT_TYPE_NOTSET,
DEVLINK_PORT_TYPE_AUTO,
DEVLINK_PORT_TYPE_ETH,
DEVLINK_PORT_TYPE_IB,
};
enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC,
/* bus name + dev name together are a handle for devlink entity */
DEVLINK_ATTR_BUS_NAME, /* string */
DEVLINK_ATTR_DEV_NAME, /* string */
DEVLINK_ATTR_PORT_INDEX, /* u32 */
DEVLINK_ATTR_PORT_TYPE, /* u16 */
DEVLINK_ATTR_PORT_DESIRED_TYPE, /* u16 */
DEVLINK_ATTR_PORT_NETDEV_IFINDEX, /* u32 */
DEVLINK_ATTR_PORT_NETDEV_NAME, /* string */
DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */
DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */
DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1
};
#endif /* _UAPI_LINUX_DEVLINK_H_ */
.TH DEVLINK\-DEV 8 "14 Mar 2016" "iproute2" "Linux"
.SH NAME
devlink-dev \- devlink device configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ]"
.B dev
.RI " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] |
.ti -8
.B devlink dev show
.RI "[ " DEV " ]"
.ti -8
.B devlink dev help
.SH "DESCRIPTION"
.SS devlink dev show - display devlink device attributes
.PP
.I "DEV"
- specifies the devlink device to show.
If this argument is omitted all devices are listed.
.in +4
Format is:
.in +2
BUS_NAME/BUS_ADDRESS
.SH "EXAMPLES"
.PP
devlink dev show
.RS 4
Shows the state of all devlink devices on the system.
.RE
.PP
devlink dev show pci/0000:01:00.0
.RS 4
Shows the state of specified devlink device.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-port (8),
.BR devlink-monitor (8),
.br
.SH AUTHOR
Jiri Pirko <jiri@mellanox.com>
.TH DEVLINK\-MONITOR 8 "14 Mar 2016" "iproute2" "Linux"
.SH "NAME"
devlink-monitor \- state monitoring
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.BR "devlink monitor" " [ " all " |"
.IR OBJECT-LIST " ]"
.sp
.SH DESCRIPTION
The
.B devlink
utility can monitor the state of devlink devices and ports
continuously. This option has a slightly different format. Namely, the
.B monitor
command is the first in the command line and then the object list.
.I OBJECT-LIST
is the list of object types that we want to monitor.
It may contain
.BR dev ", " port ".
.B devlink
opens Devlink Netlink socket, listens on it and dumps state changes.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-dev (8),
.BR devlink-port (8),
.br
.SH AUTHOR
Jiri Pirko <jiri@mellanox.com>
.TH DEVLINK\-PORT 8 "14 Mar 2016" "iproute2" "Linux"
.SH NAME
devlink-port \- devlink port configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ]"
.B port
.RI " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] |
.ti -8
.BR "devlink port set "
.IR DEV/PORT_INDEX
.RI "[ "
.BR type " { " eth " | " ib " | " auto " }"
.RI "]"
.ti -8
.BR "devlink port split "
.IR DEV/PORT_INDEX
.BR count
.IR COUNT
.ti -8
.BR "devlink port unsplit "
.IR DEV/PORT_INDEX
.ti -8
.B devlink port show
.RI "[ " DEV/PORT_INDEX " ]"
.ti -8
.B devlink port help
.SH "DESCRIPTION"
.SS devlink port set - change devlink port attributes
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port to operate on.
.in +4
Format is:
.in +2
BUS_NAME/BUS_ADDRESS/PORT_INDEX
.TP
.BR type " { " eth " | " ib " | " auto " } "
set port type
.I eth
- Ethernet
.I ib
- Infiniband
.I auto
- autoselect
.SS devlink port split - split devlink port into more
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port to operate on.
.TP
.BI count " COUNT"
number of ports to split to.
.SS devlink port unsplit - unsplit previously split devlink port
Could be performed on any split port of the same split group.
.PP
.B "DEV/PORT_INDEX"
- specifies the devlink port to operate on.
.SS devlink port show - display devlink port attributes
.PP
.I "DEV/PORT_INDEX"
- specifies the devlink port to show.
If this argument is omitted all ports are listed.
.SH "EXAMPLES"
.PP
devlink port show
.RS 4
Shows the state of all devlink ports on the system.
.RE
.PP
devlink port show pci/0000:01:00.0/1
.RS 4
Shows the state of specified devlink port.
.RE
.PP
devlink port set pci/0000:01:00.0/1 type eth
.RS 4
Set type of specified devlink port to Ethernet.
.RE
.PP
devlink port split pci/0000:01:00.0/1 count 4
.RS 4
Split the specified devlink port into four ports.
.RE
.PP
devlink port unsplit pci/0000:01:00.0/1
.RS 4
Unplit the specified previously split devlink port.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-dev (8),
.BR devlink-monitor (8),
.br
.SH AUTHOR
Jiri Pirko <jiri@mellanox.com>
.TH DEVLINK 8 "14 Mar 2016" "iproute2" "Linux"
.SH NAME
devlink \- Devlink tool
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.IR OBJECT " := { "
.BR dev " | " port " | " monitor " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] |
.SH OPTIONS
.TP
.BR "\-V" , " -Version"
Print the version of the
.B devlink
utility and exit.
.SS
.I OBJECT
.TP
.B dev
- devlink device.
.TP
.B port
- devlink port.
.TP
.B monitor
- watch for netlink messages.
.SS
.I COMMAND
Specifies the action to perform on the object.
The set of possible actions depends on the object type.
As a rule, it is possible to
.B show
(or
.B list
) objects, but some objects do not allow all of these operations
or have some additional commands. The
.B help
command is available for all objects. It prints
out a list of available commands and argument syntax conventions.
.sp
If no command is given, some default command is assumed.
Usually it is
.B list
or, if the objects of this class cannot be listed,
.BR "help" .
.SH EXIT STATUS
Exit status is 0 if command was successful or a positive integer upon failure.
.SH SEE ALSO
.BR devlink-dev (8),
.BR devlink-port (8),
.BR devlink-monitor (8),
.br
.SH REPORTING BUGS
Report any bugs to the Network Developers mailing list
.B <netdev@vger.kernel.org>
where the development and maintenance is primarily done.
You do not have to be subscribed to the list to send a message there.
.SH AUTHOR
Jiri Pirko <jiri@mellanox.com>
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