]> git.proxmox.com Git - mirror_iproute2.git/blob - genl/ctrl.c
This patch adds a generic netlink controller interface.
[mirror_iproute2.git] / genl / ctrl.c
1 /*
2 * ctrl.c generic netlink controller
3 *
4 * This program is free software; you can distribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: J Hadi Salim (hadi@cyberus.ca)
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <syslog.h>
16 #include <fcntl.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <string.h>
21
22 #include "utils.h"
23 #include "genl_utils.h"
24
25 static int usage(void)
26 {
27 fprintf(stderr,"Usage: ctrl <CMD>\n" \
28 "CMD := get <PARMS> | list | monitor\n" \
29 "PARMS := name <name> | id <id>\n" \
30 "Examples:\n" \
31 "\tctrl ls\n" \
32 "\tctrl monitor\n" \
33 "\tctrl get name foobar\n" \
34 "\tctrl get id 0xF\n");
35 return -1;
36 }
37
38 int genl_ctrl_resolve_family(const char *family)
39 {
40 struct rtnl_handle rth;
41 struct nlmsghdr *nlh;
42 struct genlmsghdr *ghdr;
43 int ret = 0;
44 struct {
45 struct nlmsghdr n;
46 char buf[4096];
47 } req;
48
49 memset(&req, 0, sizeof(req));
50
51 nlh = &req.n;
52 nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
53 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
54 nlh->nlmsg_type = GENL_ID_CTRL;
55
56 ghdr = NLMSG_DATA(&req.n);
57 ghdr->cmd = CTRL_CMD_GETFAMILY;
58
59 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
60 fprintf(stderr, "Cannot open generic netlink socket\n");
61 exit(1);
62 }
63
64 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
65
66 if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
67 fprintf(stderr, "Error talking to the kernel\n");
68 goto errout;
69 }
70
71 {
72 struct rtattr *tb[CTRL_ATTR_MAX + 1];
73 struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
74 int len = nlh->nlmsg_len;
75 struct rtattr *attrs;
76
77 if (nlh->nlmsg_type != GENL_ID_CTRL) {
78 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
79 "nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
80 goto errout;
81 }
82
83 if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
84 fprintf(stderr, "Unkown controller command %d\n", ghdr->cmd);
85 goto errout;
86 }
87
88 len -= NLMSG_LENGTH(GENL_HDRLEN);
89
90 if (len < 0) {
91 fprintf(stderr, "wrong controller message len %d\n", len);
92 return -1;
93 }
94
95 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
96 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
97
98 if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
99 fprintf(stderr, "Missing family id TLV\n");
100 goto errout;
101 }
102
103 ret = *(__u16 *) RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
104 }
105
106 errout:
107 rtnl_close(&rth);
108 return ret;
109 }
110
111 static int print_ctrl(const struct sockaddr_nl *who, struct nlmsghdr *n,
112 void *arg)
113 {
114 struct rtattr *tb[CTRL_ATTR_MAX + 1];
115 struct genlmsghdr *ghdr = NLMSG_DATA(n);
116 int len = n->nlmsg_len;
117 struct rtattr *attrs;
118 FILE *fp = (FILE *) arg;
119
120 if (n->nlmsg_type != GENL_ID_CTRL) {
121 fprintf(stderr, "Not a controller message, nlmsg_len=%d "
122 "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
123 return 0;
124 }
125
126 if (ghdr->cmd != CTRL_CMD_GETFAMILY &&
127 ghdr->cmd != CTRL_CMD_DELFAMILY &&
128 ghdr->cmd != CTRL_CMD_NEWFAMILY) {
129 fprintf(stderr, "Unkown controller command %d\n", ghdr->cmd);
130 return 0;
131 }
132
133 len -= NLMSG_LENGTH(GENL_HDRLEN);
134
135 if (len < 0) {
136 fprintf(stderr, "wrong controller message len %d\n", len);
137 return -1;
138 }
139
140 attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
141 parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
142
143 if (tb[CTRL_ATTR_FAMILY_NAME]) {
144 char *name = RTA_DATA(tb[CTRL_ATTR_FAMILY_NAME]);
145 fprintf(fp, "Name: %s\n",name);
146 }
147 if (tb[CTRL_ATTR_FAMILY_ID]) {
148 __u16 *id = RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]);
149 fprintf(fp, "ID: 0x%x\n",*id);
150 }
151
152 fflush(fp);
153 return 0;
154 }
155
156 static int ctrl_list(int cmd, int argc, char **argv)
157 {
158 struct rtnl_handle rth;
159 struct nlmsghdr *nlh;
160 struct genlmsghdr *ghdr;
161 int ret = -1;
162 char d[GENL_NAMSIZ];
163 struct {
164 struct nlmsghdr n;
165 char buf[4096];
166 } req;
167
168 memset(&req, 0, sizeof(req));
169
170 nlh = &req.n;
171 nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
172 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
173 nlh->nlmsg_type = GENL_ID_CTRL;
174
175 ghdr = NLMSG_DATA(&req.n);
176 ghdr->cmd = CTRL_CMD_GETFAMILY;
177
178 if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
179 fprintf(stderr, "Cannot open generic netlink socket\n");
180 exit(1);
181 }
182
183 if (cmd == CTRL_CMD_GETFAMILY) {
184 if (argc != 2) {
185 fprintf(stderr, "Wrong number of params\n");
186 return -1;
187 }
188
189 if (matches(*argv, "name") == 0) {
190 NEXT_ARG();
191 strncpy(d, *argv, sizeof (d) - 1);
192 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
193 d, strlen(d) + 1);
194 } else if (matches(*argv, "id") == 0) {
195 __u16 id;
196 NEXT_ARG();
197 if (get_u16(&id, *argv, 0)) {
198 fprintf(stderr, "Illegal \"id\"\n");
199 goto ctrl_done;
200 }
201
202 addattr_l(nlh, 128, CTRL_ATTR_FAMILY_ID, &id, 2);
203
204 } else {
205 fprintf(stderr, "Wrong params\n");
206 goto ctrl_done;
207 }
208
209 if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
210 fprintf(stderr, "Error talking to the kernel\n");
211 goto ctrl_done;
212 }
213
214 if (print_ctrl(NULL, nlh, (void *) stdout) < 0) {
215 fprintf(stderr, "Dump terminated\n");
216 goto ctrl_done;
217 }
218
219 }
220
221 if (cmd == CTRL_CMD_UNSPEC) {
222 nlh->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
223 nlh->nlmsg_seq = rth.dump = ++rth.seq;
224
225 if (rtnl_send(&rth, (const char *) nlh, nlh->nlmsg_len) < 0) {
226 perror("Failed to send dump request\n");
227 goto ctrl_done;
228 }
229
230 rtnl_dump_filter(&rth, print_ctrl, stdout, NULL, NULL);
231
232 }
233
234 ret = 0;
235 ctrl_done:
236 rtnl_close(&rth);
237 return ret;
238 }
239
240 static int ctrl_listen(int argc, char **argv)
241 {
242 struct rtnl_handle rth;
243
244 if (rtnl_open_byproto(&rth, nl_mgrp(GENL_ID_CTRL), NETLINK_GENERIC) < 0) {
245 fprintf(stderr, "Canot open generic netlink socket\n");
246 return -1;
247 }
248
249 if (rtnl_listen(&rth, print_ctrl, (void *) stdout) < 0)
250 return -1;
251
252 return 0;
253 }
254
255 static int parse_ctrl(struct genl_util *a, int argc, char **argv)
256 {
257 argv++;
258 if (--argc <= 0) {
259 fprintf(stderr, "wrong controller params\n");
260 return -1;
261 }
262
263 if (matches(*argv, "monitor") == 0)
264 return ctrl_listen(argc-1, argv+1);
265 if (matches(*argv, "get") == 0)
266 return ctrl_list(CTRL_CMD_GETFAMILY, argc-1, argv+1);
267 if (matches(*argv, "list") == 0 ||
268 matches(*argv, "show") == 0 ||
269 matches(*argv, "lst") == 0)
270 return ctrl_list(CTRL_CMD_UNSPEC, argc-1, argv+1);
271 if (matches(*argv, "help") == 0)
272 return usage();
273
274 fprintf(stderr, "ctrl command \"%s\" is unknown, try \"ctrl -help\".\n",
275 *argv);
276
277 return -1;
278 }
279
280 struct genl_util ctrl_genl_util = {
281 .name = "ctrl",
282 .parse_genlopt = parse_ctrl,
283 .print_genlopt = print_ctrl,
284 };