mirror_iproute2/genl/ctrl.c
Hangbin Liu 86bf43c7c2 lib/libnetlink: update rtnl_talk to support malloc buff at run time
This is an update for 460c03f3f3 ("iplink: double the buffer size also in
iplink_get()"). After update, we will not need to double the buffer size
every time when VFs number increased.

With call like rtnl_talk(&rth, &req.n, NULL, 0), we can simply remove the
length parameter.

With call like rtnl_talk(&rth, nlh, nlh, sizeof(req), I add a new variable
answer to avoid overwrite data in nlh, because it may has more info after
nlh. also this will avoid nlh buffer not enough issue.

We need to free answer after using.

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Signed-off-by: Phil Sutter <phil@nwl.cc>
2017-10-26 12:29:29 +02:00

415 lines
9.6 KiB
C

/*
* ctrl.c generic netlink controller
*
* This program is free software; you can distribute 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: J Hadi Salim (hadi@cyberus.ca)
* Johannes Berg (johannes@sipsolutions.net)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "utils.h"
#include "genl_utils.h"
#define GENL_MAX_FAM_OPS 256
#define GENL_MAX_FAM_GRPS 256
static int usage(void)
{
fprintf(stderr,"Usage: ctrl <CMD>\n" \
"CMD := get <PARMS> | list | monitor\n" \
"PARMS := name <name> | id <id>\n" \
"Examples:\n" \
"\tctrl ls\n" \
"\tctrl monitor\n" \
"\tctrl get name foobar\n" \
"\tctrl get id 0xF\n");
return -1;
}
int genl_ctrl_resolve_family(const char *family)
{
struct rtnl_handle rth;
int ret = 0;
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[4096];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
.n.nlmsg_type = GENL_ID_CTRL,
.g.cmd = CTRL_CMD_GETFAMILY,
};
struct nlmsghdr *nlh = &req.n;
struct genlmsghdr *ghdr = &req.g;
struct nlmsghdr *answer = NULL;
if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
fprintf(stderr, "Cannot open generic netlink socket\n");
exit(1);
}
addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
if (rtnl_talk(&rth, nlh, &answer) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
goto errout;
}
{
struct rtattr *tb[CTRL_ATTR_MAX + 1];
int len = answer->nlmsg_len;
struct rtattr *attrs;
if (answer->nlmsg_type != GENL_ID_CTRL) {
fprintf(stderr, "Not a controller message, nlmsg_len=%d "
"nlmsg_type=0x%x\n", answer->nlmsg_len, answer->nlmsg_type);
goto errout;
}
if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
goto errout;
}
len -= NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
fprintf(stderr, "wrong controller message len %d\n", len);
free(answer);
return -1;
}
attrs = (struct rtattr *) ((char *) answer + NLMSG_LENGTH(GENL_HDRLEN));
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
fprintf(stderr, "Missing family id TLV\n");
goto errout;
}
ret = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
}
errout:
free(answer);
rtnl_close(&rth);
return ret;
}
static void print_ctrl_cmd_flags(FILE *fp, __u32 fl)
{
fprintf(fp, "\n\t\tCapabilities (0x%x):\n ", fl);
if (!fl) {
fprintf(fp, "\n");
return;
}
fprintf(fp, "\t\t ");
if (fl & GENL_ADMIN_PERM)
fprintf(fp, " requires admin permission;");
if (fl & GENL_CMD_CAP_DO)
fprintf(fp, " can doit;");
if (fl & GENL_CMD_CAP_DUMP)
fprintf(fp, " can dumpit;");
if (fl & GENL_CMD_CAP_HASPOL)
fprintf(fp, " has policy");
fprintf(fp, "\n");
}
static int print_ctrl_cmds(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
{
struct rtattr *tb[CTRL_ATTR_OP_MAX + 1];
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg);
if (tb[CTRL_ATTR_OP_ID]) {
__u32 *id = RTA_DATA(tb[CTRL_ATTR_OP_ID]);
fprintf(fp, " ID-0x%x ",*id);
}
/* we are only gonna do this for newer version of the controller */
if (tb[CTRL_ATTR_OP_FLAGS] && ctrl_ver >= 0x2) {
__u32 *fl = RTA_DATA(tb[CTRL_ATTR_OP_FLAGS]);
print_ctrl_cmd_flags(fp, *fl);
}
return 0;
}
static int print_ctrl_grp(FILE *fp, struct rtattr *arg, __u32 ctrl_ver)
{
struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
if (arg == NULL)
return -1;
parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
if (tb[2]) {
__u32 *id = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]);
fprintf(fp, " ID-0x%x ",*id);
}
if (tb[1]) {
char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
fprintf(fp, " name: %s ", name);
}
return 0;
}
/*
* The controller sends one nlmsg per family
*/
static int print_ctrl(const struct sockaddr_nl *who,
struct rtnl_ctrl_data *ctrl,
struct nlmsghdr *n, void *arg)
{
struct rtattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *ghdr = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *attrs;
FILE *fp = (FILE *) arg;
__u32 ctrl_v = 0x1;
if (n->nlmsg_type != GENL_ID_CTRL) {
fprintf(stderr, "Not a controller message, nlmsg_len=%d "
"nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
return 0;
}
if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
ghdr->cmd != CTRL_CMD_DELFAMILY &&
ghdr->cmd != CTRL_CMD_NEWFAMILY &&
ghdr->cmd != CTRL_CMD_NEWMCAST_GRP &&
ghdr->cmd != CTRL_CMD_DELMCAST_GRP) {
fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
return 0;
}
len -= NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
fprintf(stderr, "wrong controller message len %d\n", len);
return -1;
}
attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
if (tb[CTRL_ATTR_FAMILY_NAME]) {
char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
fprintf(fp, "\nName: %s\n",name);
}
if (tb[CTRL_ATTR_FAMILY_ID]) {
__u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
fprintf(fp, "\tID: 0x%x ",*id);
}
if (tb[CTRL_ATTR_VERSION]) {
__u32 *v = RTA_DATA(tb[CTRL_ATTR_VERSION]);
fprintf(fp, " Version: 0x%x ",*v);
ctrl_v = *v;
}
if (tb[CTRL_ATTR_HDRSIZE]) {
__u32 *h = RTA_DATA(tb[CTRL_ATTR_HDRSIZE]);
fprintf(fp, " header size: %d ",*h);
}
if (tb[CTRL_ATTR_MAXATTR]) {
__u32 *ma = RTA_DATA(tb[CTRL_ATTR_MAXATTR]);
fprintf(fp, " max attribs: %d ",*ma);
}
/* end of family definitions .. */
fprintf(fp,"\n");
if (tb[CTRL_ATTR_OPS]) {
struct rtattr *tb2[GENL_MAX_FAM_OPS];
int i=0;
parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, tb[CTRL_ATTR_OPS]);
fprintf(fp, "\tcommands supported: \n");
for (i = 0; i < GENL_MAX_FAM_OPS; i++) {
if (tb2[i]) {
fprintf(fp, "\t\t#%d: ", i);
if (0 > print_ctrl_cmds(fp, tb2[i], ctrl_v)) {
fprintf(fp, "Error printing command\n");
}
/* for next command */
fprintf(fp,"\n");
}
}
/* end of family::cmds definitions .. */
fprintf(fp,"\n");
}
if (tb[CTRL_ATTR_MCAST_GROUPS]) {
struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
int i;
parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
tb[CTRL_ATTR_MCAST_GROUPS]);
fprintf(fp, "\tmulticast groups:\n");
for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
if (tb2[i]) {
fprintf(fp, "\t\t#%d: ", i);
if (0 > print_ctrl_grp(fp, tb2[i], ctrl_v))
fprintf(fp, "Error printing group\n");
/* for next group */
fprintf(fp,"\n");
}
}
/* end of family::groups definitions .. */
fprintf(fp,"\n");
}
fflush(fp);
return 0;
}
static int print_ctrl2(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg)
{
return print_ctrl(who, NULL, n, arg);
}
static int ctrl_list(int cmd, int argc, char **argv)
{
struct rtnl_handle rth;
int ret = -1;
char d[GENL_NAMSIZ];
struct {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[4096];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN),
.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
.n.nlmsg_type = GENL_ID_CTRL,
.g.cmd = CTRL_CMD_GETFAMILY,
};
struct nlmsghdr *nlh = &req.n;
struct nlmsghdr *answer = NULL;
if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
fprintf(stderr, "Cannot open generic netlink socket\n");
exit(1);
}
if (cmd == CTRL_CMD_GETFAMILY) {
if (argc != 2) {
fprintf(stderr, "Wrong number of params\n");
return -1;
}
if (matches(*argv, "name") == 0) {
NEXT_ARG();
strlcpy(d, *argv, sizeof(d));
addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
d, strlen(d) + 1);
} else if (matches(*argv, "id") == 0) {
__u16 id;
NEXT_ARG();
if (get_u16(&id, *argv, 0)) {
fprintf(stderr, "Illegal \"id\"\n");
goto ctrl_done;
}
addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
} else {
fprintf(stderr, "Wrong params\n");
goto ctrl_done;
}
if (rtnl_talk(&rth, nlh, &answer) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
goto ctrl_done;
}
if (print_ctrl2(NULL, answer, (void *) stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
goto ctrl_done;
}
}
if (cmd == CTRL_CMD_UNSPEC) {
nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
nlh->nlmsg_seq = rth.dump = ++rth.seq;
if (rtnl_send(&rth, nlh, nlh->nlmsg_len) < 0) {
perror("Failed to send dump request\n");
goto ctrl_done;
}
rtnl_dump_filter(&rth, print_ctrl2, stdout);
}
ret = 0;
ctrl_done:
free(answer);
rtnl_close(&rth);
return ret;
}
static int ctrl_listen(int argc, char **argv)
{
struct rtnl_handle rth;
if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
fprintf(stderr, "Canot open generic netlink socket\n");
return -1;
}
if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
return -1;
return 0;
}
static int parse_ctrl(struct genl_util *a, int argc, char **argv)
{
argv++;
if (--argc <= 0) {
fprintf(stderr, "wrong controller params\n");
return -1;
}
if (matches(*argv, "monitor") == 0)
return ctrl_listen(argc-1, argv+1);
if (matches(*argv, "get") == 0)
return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
if (matches(*argv, "list") == 0 ||
matches(*argv, "show") == 0 ||
matches(*argv, "lst") == 0)
return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
if (matches(*argv, "help") == 0)
return usage();
fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl help\".\n",
*argv);
return -1;
}
struct genl_util ctrl_genl_util = {
.name = "ctrl",
.parse_genlopt = parse_ctrl,
.print_genlopt = print_ctrl2,
};