/
githubmirror
/
iproute2
Обзор
Документация
Войти
/
githubmirror
/
iproute2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
genl/ctrl.c
487 строк
12 KB
Stephen Hemminger
libnetlink: flush stdout only in monitor mode
27 апр 2026, 21:39
27 апр 2026, 21:39
b468824
Код
Авторство
О чём код?
/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * ctrl.c generic netlink controller * * Authors: J Hadi Salim (hadi@cyberus.ca) * Johannes Berg (johannes@sipsolutions.net) */ #include <stdio.h> #include <stdlib.h> #include <unistd.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 | policy <PARMS>\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" "\tctrl policy name foobar\n" "\tctrl policy id 0xF\n"); return -1; } static void print_ctrl_flag(const char *json_str, const char *fp_str) { print_string(PRINT_JSON, NULL, NULL, json_str); print_string(PRINT_FP, NULL, " %s", fp_str); } static void print_ctrl_cmd_flags(__u32 fl) { print_0xhex(PRINT_FP, "flags", "\n\t\tCapabilities (0x%x):\n ", fl); open_json_array(PRINT_JSON, "capabilities"); if (fl != 0) print_string(PRINT_FP, NULL, "\t\t ", NULL); if (fl & GENL_ADMIN_PERM) print_ctrl_flag("admin", "requires admin permission;"); if (fl & GENL_CMD_CAP_DO) print_ctrl_flag("do", "can doit;"); if (fl & GENL_CMD_CAP_DUMP) print_ctrl_flag("dump", "can dumpit;"); if (fl & GENL_CMD_CAP_HASPOL) print_ctrl_flag("policy", "has policy"); close_json_array(PRINT_ANY, "\n"); } static void print_ctrl_cmd(const struct rtattr *arg) { struct rtattr *tb[CTRL_ATTR_OP_MAX + 1]; parse_rtattr_nested(tb, CTRL_ATTR_OP_MAX, arg); if (tb[CTRL_ATTR_OP_ID]) print_0xhex(PRINT_ANY, "id", " ID-0x%x ", rta_getattr_u32(tb[CTRL_ATTR_OP_ID])); /* we are only gonna do this for newer version of the controller */ if (tb[CTRL_ATTR_OP_FLAGS]) print_ctrl_cmd_flags(rta_getattr_u32(tb[CTRL_ATTR_OP_FLAGS])); } static void print_ctrl_ops(const struct rtattr *attr) { struct rtattr *tb2[GENL_MAX_FAM_OPS]; unsigned int i; parse_rtattr_nested(tb2, GENL_MAX_FAM_OPS, attr); open_json_array(PRINT_JSON, "operations"); print_string(PRINT_FP, NULL, "\tcommands supported: \n", NULL); for (i = 0; i < GENL_MAX_FAM_OPS; i++) { if (!tb2[i]) continue; open_json_object(NULL); print_uint(PRINT_FP, NULL, "\t\t#%u: ", i); print_ctrl_cmd(tb2[i]); print_string(PRINT_FP, NULL, "\n", NULL); close_json_object(); } /* end of family::cmds definitions .. */ close_json_array(PRINT_JSON, NULL); print_string(PRINT_FP, NULL, "\n", NULL); } static void print_ctrl_grp(const struct rtattr *arg) { struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1]; open_json_object(NULL); parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg); if (tb[CTRL_ATTR_MCAST_GRP_ID]) print_0xhex(PRINT_ANY, "id", " ID-0x%x ", rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID])); if (tb[CTRL_ATTR_MCAST_GRP_NAME]) { const char *name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]); print_string(PRINT_ANY, "name", " name: %s ", name); } close_json_object(); } static void print_ops(const struct rtattr *attr) { const struct rtattr *pos; open_json_array(PRINT_JSON, "op"); rtattr_for_each_nested(pos, attr) { struct rtattr *ptb[CTRL_ATTR_POLICY_DUMP_MAX + 1]; struct rtattr *pattrs = RTA_DATA(pos); int plen = RTA_PAYLOAD(pos); parse_rtattr_flags(ptb, CTRL_ATTR_POLICY_DUMP_MAX, pattrs, plen, NLA_F_NESTED); print_uint(PRINT_ANY, "bits", " op %d policies:", pos->rta_type & ~NLA_F_NESTED); if (ptb[CTRL_ATTR_POLICY_DO]) print_uint(PRINT_ANY, "do", " do=%u", rta_getattr_u32(ptb[CTRL_ATTR_POLICY_DO])); if (ptb[CTRL_ATTR_POLICY_DUMP]) print_uint(PRINT_ANY, "dump", " dump=%d", rta_getattr_u32(ptb[CTRL_ATTR_POLICY_DUMP])); } close_json_array(PRINT_JSON, NULL); } static void print_ctrl_mcast(const struct rtattr *attr) { struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1]; unsigned int i; parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS, attr); open_json_array(PRINT_JSON, "mcast"); print_string(PRINT_FP, NULL, "\tmulticast groups:\n", NULL); for (i = 0; i < GENL_MAX_FAM_GRPS; i++) { if (!tb2[i]) continue; print_uint(PRINT_FP, NULL, "\t\t#%d: ", i); print_ctrl_grp(tb2[i]); /* for next group */ print_string(PRINT_FP, NULL, "\n", NULL); } /* end of family::groups definitions .. */ close_json_array(PRINT_JSON, NULL); print_string(PRINT_FP, NULL, "\n", NULL); } static const char *get_nla_type_str(unsigned int attr) { switch (attr) { #define C(x) case NL_ATTR_TYPE_ ## x: return #x C(U8); C(U16); C(U32); C(U64); C(STRING); C(FLAG); C(NESTED); C(NESTED_ARRAY); C(NUL_STRING); C(BINARY); C(S8); C(S16); C(S32); C(S64); C(BITFIELD32); default: return "unknown"; } } static void print_policy_attr(const struct rtattr *attr) { struct rtattr *tp[NL_POLICY_TYPE_ATTR_MAX + 1]; parse_rtattr_nested(tp, ARRAY_SIZE(tp) - 1, attr); if (tp[NL_POLICY_TYPE_ATTR_TYPE]) { print_uint(PRINT_ANY, "attr", "attr[%u]:", attr->rta_type & ~NLA_F_NESTED); print_string(PRINT_ANY, "type", " type=%s", get_nla_type_str(rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_TYPE]))); } if (tp[NL_POLICY_TYPE_ATTR_POLICY_IDX]) print_uint(PRINT_ANY, "policy", " policy:%u", rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_IDX])); if (tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE]) print_uint(PRINT_ANY, "maxattr", " maxattr:%u", rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE])); if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S]) { print_s64(PRINT_ANY, "min_value", " range:[%lld", rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_S])); print_s64(PRINT_ANY, "max_value", "%lld]", rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_S])); } if (tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U] && tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U]) { print_u64(PRINT_ANY, "min_value", " range:[%llu", rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MIN_VALUE_U])); print_u64(PRINT_ANY, "max_value", "%llu]", rta_getattr_u64(tp[NL_POLICY_TYPE_ATTR_MAX_VALUE_U])); } if (tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH]) print_uint(PRINT_ANY, "min_length", " min len:%u", rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MIN_LENGTH])); if (tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH]) print_uint(PRINT_ANY, "max_length", " max len:%u", rta_getattr_u32(tp[NL_POLICY_TYPE_ATTR_MAX_LENGTH])); } static void print_policy(const struct rtattr *attr) { const struct rtattr *pos; open_json_array(PRINT_JSON, NULL); rtattr_for_each_nested(pos, attr) { const struct rtattr *a; open_json_array(PRINT_JSON, NULL); print_uint(PRINT_ANY, "policy", " policy[%u]:", pos->rta_type & ~NLA_F_NESTED); rtattr_for_each_nested(a, pos) { open_json_object(NULL); print_policy_attr(a); close_json_object(); } close_json_array(PRINT_JSON, NULL); } close_json_array(PRINT_JSON, NULL); } /* * The controller sends one nlmsg per family */ static int print_ctrl(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; 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 && ghdr->cmd != CTRL_CMD_GETPOLICY) { 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_flags(tb, CTRL_ATTR_MAX, attrs, len, NLA_F_NESTED); if (tb[CTRL_ATTR_FAMILY_NAME]) { const char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]); print_string(PRINT_ANY, "family", "\nName: %s\n", name); } if (tb[CTRL_ATTR_FAMILY_ID]) print_0xhex(PRINT_ANY, "id", "\tID: 0x%x ", rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID])); if (tb[CTRL_ATTR_VERSION]) print_0xhex(PRINT_ANY, "version", " Version: 0x%x ", rta_getattr_u32(tb[CTRL_ATTR_VERSION])); if (tb[CTRL_ATTR_HDRSIZE]) print_uint(PRINT_ANY, "header_size", " header size: %u ", rta_getattr_u32(tb[CTRL_ATTR_HDRSIZE])); if (tb[CTRL_ATTR_MAXATTR]) print_uint(PRINT_ANY, "max_attr", " max attribs: %u ", rta_getattr_u32(tb[CTRL_ATTR_MAXATTR])); if (tb[CTRL_ATTR_OP_POLICY]) print_ops(tb[CTRL_ATTR_OP_POLICY]); if (tb[CTRL_ATTR_POLICY]) print_policy(tb[CTRL_ATTR_POLICY]); /* end of family definitions .. */ print_string(PRINT_FP, NULL, "\n", NULL); if (tb[CTRL_ATTR_OPS]) print_ctrl_ops(tb[CTRL_ATTR_OPS]); if (tb[CTRL_ATTR_MCAST_GROUPS]) print_ctrl_mcast(tb[CTRL_ATTR_MCAST_GROUPS]); return 0; } static int print_ctrl2(struct nlmsghdr *n, void *arg) { open_json_object(NULL); print_ctrl(NULL, n, arg); close_json_object(); return 0; } 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 || cmd == CTRL_CMD_GETPOLICY) { req.g.cmd = cmd; if (argc != 2) { fprintf(stderr, "Wrong number of params\n"); goto ctrl_done; } 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; } } new_json_obj(json); if (cmd == CTRL_CMD_GETFAMILY) { if (rtnl_talk(&rth, nlh, &answer) < 0) { fprintf(stderr, "Error talking to the kernel\n"); goto ctrl_done; } if (print_ctrl2(answer, (void *) stdout) < 0) { fprintf(stderr, "Dump terminated\n"); goto ctrl_done; } } if (cmd == CTRL_CMD_UNSPEC || cmd == CTRL_CMD_GETPOLICY) { 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: delete_json_obj(); 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, "Cannot open generic netlink socket\n"); return -1; } if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0) exit(2); rtnl_close(&rth); 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, "policy") == 0) return ctrl_list(CTRL_CMD_GETPOLICY, 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, };