]>
git.proxmox.com Git - mirror_iproute2.git/blob - devlink/mnlg.c
2 * mnlg.c Generic Netlink helpers for libmnl
4 * This program is free software; you can redistribute 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.
9 * Authors: Jiri Pirko <jiri@mellanox.com>
18 #include <libmnl/libmnl.h>
19 #include <linux/genetlink.h>
21 #include "libnetlink.h"
26 struct mnl_socket
*nl
;
34 static struct nlmsghdr
*__mnlg_msg_prepare(struct mnlg_socket
*nlg
, uint8_t cmd
,
35 uint16_t flags
, uint32_t id
,
39 struct genlmsghdr
*genl
;
41 nlh
= mnl_nlmsg_put_header(nlg
->buf
);
43 nlh
->nlmsg_flags
= flags
;
44 nlg
->seq
= time(NULL
);
45 nlh
->nlmsg_seq
= nlg
->seq
;
47 genl
= mnl_nlmsg_put_extra_header(nlh
, sizeof(struct genlmsghdr
));
49 genl
->version
= version
;
54 struct nlmsghdr
*mnlg_msg_prepare(struct mnlg_socket
*nlg
, uint8_t cmd
,
57 return __mnlg_msg_prepare(nlg
, cmd
, flags
, nlg
->id
, nlg
->version
);
60 int mnlg_socket_send(struct mnlg_socket
*nlg
, const struct nlmsghdr
*nlh
)
62 return mnl_socket_sendto(nlg
->nl
, nlh
, nlh
->nlmsg_len
);
65 static int mnlg_cb_noop(const struct nlmsghdr
*nlh
, void *data
)
70 static int mnlg_cb_error(const struct nlmsghdr
*nlh
, void *data
)
72 const struct nlmsgerr
*err
= mnl_nlmsg_get_payload(nlh
);
74 /* Netlink subsystems returns the errno value with different signess */
80 if (nl_dump_ext_ack(nlh
, NULL
))
83 return err
->error
== 0 ? MNL_CB_STOP
: MNL_CB_ERROR
;
86 static int mnlg_cb_stop(const struct nlmsghdr
*nlh
, void *data
)
91 static mnl_cb_t mnlg_cb_array
[NLMSG_MIN_TYPE
] = {
92 [NLMSG_NOOP
] = mnlg_cb_noop
,
93 [NLMSG_ERROR
] = mnlg_cb_error
,
94 [NLMSG_DONE
] = mnlg_cb_stop
,
95 [NLMSG_OVERRUN
] = mnlg_cb_noop
,
98 int mnlg_socket_recv_run(struct mnlg_socket
*nlg
, mnl_cb_t data_cb
, void *data
)
103 err
= mnl_socket_recvfrom(nlg
->nl
, nlg
->buf
,
104 MNL_SOCKET_BUFFER_SIZE
);
107 err
= mnl_cb_run2(nlg
->buf
, err
, nlg
->seq
, nlg
->portid
,
108 data_cb
, data
, mnlg_cb_array
,
109 ARRAY_SIZE(mnlg_cb_array
));
121 static int parse_mc_grps_cb(const struct nlattr
*attr
, void *data
)
123 const struct nlattr
**tb
= data
;
124 int type
= mnl_attr_get_type(attr
);
126 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MCAST_GRP_MAX
) < 0)
130 case CTRL_ATTR_MCAST_GRP_ID
:
131 if (mnl_attr_validate(attr
, MNL_TYPE_U32
) < 0)
134 case CTRL_ATTR_MCAST_GRP_NAME
:
135 if (mnl_attr_validate(attr
, MNL_TYPE_STRING
) < 0)
143 static void parse_genl_mc_grps(struct nlattr
*nested
,
144 struct group_info
*group_info
)
149 mnl_attr_for_each_nested(pos
, nested
) {
150 struct nlattr
*tb
[CTRL_ATTR_MCAST_GRP_MAX
+ 1] = {};
152 mnl_attr_parse_nested(pos
, parse_mc_grps_cb
, tb
);
153 if (!tb
[CTRL_ATTR_MCAST_GRP_NAME
] ||
154 !tb
[CTRL_ATTR_MCAST_GRP_ID
])
157 name
= mnl_attr_get_str(tb
[CTRL_ATTR_MCAST_GRP_NAME
]);
158 if (strcmp(name
, group_info
->name
) != 0)
161 group_info
->id
= mnl_attr_get_u32(tb
[CTRL_ATTR_MCAST_GRP_ID
]);
162 group_info
->found
= true;
166 static int get_group_id_attr_cb(const struct nlattr
*attr
, void *data
)
168 const struct nlattr
**tb
= data
;
169 int type
= mnl_attr_get_type(attr
);
171 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MAX
) < 0)
174 if (type
== CTRL_ATTR_MCAST_GROUPS
&&
175 mnl_attr_validate(attr
, MNL_TYPE_NESTED
) < 0)
181 static int get_group_id_cb(const struct nlmsghdr
*nlh
, void *data
)
183 struct group_info
*group_info
= data
;
184 struct nlattr
*tb
[CTRL_ATTR_MAX
+ 1] = {};
185 struct genlmsghdr
*genl
= mnl_nlmsg_get_payload(nlh
);
187 mnl_attr_parse(nlh
, sizeof(*genl
), get_group_id_attr_cb
, tb
);
188 if (!tb
[CTRL_ATTR_MCAST_GROUPS
])
190 parse_genl_mc_grps(tb
[CTRL_ATTR_MCAST_GROUPS
], group_info
);
194 int mnlg_socket_group_add(struct mnlg_socket
*nlg
, const char *group_name
)
196 struct nlmsghdr
*nlh
;
197 struct group_info group_info
;
200 nlh
= __mnlg_msg_prepare(nlg
, CTRL_CMD_GETFAMILY
,
201 NLM_F_REQUEST
| NLM_F_ACK
, GENL_ID_CTRL
, 1);
202 mnl_attr_put_u16(nlh
, CTRL_ATTR_FAMILY_ID
, nlg
->id
);
204 err
= mnlg_socket_send(nlg
, nlh
);
208 group_info
.found
= false;
209 group_info
.name
= group_name
;
210 err
= mnlg_socket_recv_run(nlg
, get_group_id_cb
, &group_info
);
214 if (!group_info
.found
) {
219 err
= mnl_socket_setsockopt(nlg
->nl
, NETLINK_ADD_MEMBERSHIP
,
220 &group_info
.id
, sizeof(group_info
.id
));
227 static int get_family_id_attr_cb(const struct nlattr
*attr
, void *data
)
229 const struct nlattr
**tb
= data
;
230 int type
= mnl_attr_get_type(attr
);
232 if (mnl_attr_type_valid(attr
, CTRL_ATTR_MAX
) < 0)
235 if (type
== CTRL_ATTR_FAMILY_ID
&&
236 mnl_attr_validate(attr
, MNL_TYPE_U16
) < 0)
242 static int get_family_id_cb(const struct nlmsghdr
*nlh
, void *data
)
244 uint32_t *p_id
= data
;
245 struct nlattr
*tb
[CTRL_ATTR_MAX
+ 1] = {};
246 struct genlmsghdr
*genl
= mnl_nlmsg_get_payload(nlh
);
248 mnl_attr_parse(nlh
, sizeof(*genl
), get_family_id_attr_cb
, tb
);
249 if (!tb
[CTRL_ATTR_FAMILY_ID
])
251 *p_id
= mnl_attr_get_u16(tb
[CTRL_ATTR_FAMILY_ID
]);
255 struct mnlg_socket
*mnlg_socket_open(const char *family_name
, uint8_t version
)
257 struct mnlg_socket
*nlg
;
258 struct nlmsghdr
*nlh
;
262 nlg
= malloc(sizeof(*nlg
));
266 nlg
->buf
= malloc(MNL_SOCKET_BUFFER_SIZE
);
270 nlg
->nl
= mnl_socket_open(NETLINK_GENERIC
);
272 goto err_mnl_socket_open
;
274 /* Older kernels may no support capped/extended ACK reporting */
275 mnl_socket_setsockopt(nlg
->nl
, NETLINK_CAP_ACK
, &one
, sizeof(one
));
276 mnl_socket_setsockopt(nlg
->nl
, NETLINK_EXT_ACK
, &one
, sizeof(one
));
278 err
= mnl_socket_bind(nlg
->nl
, 0, MNL_SOCKET_AUTOPID
);
280 goto err_mnl_socket_bind
;
282 nlg
->portid
= mnl_socket_get_portid(nlg
->nl
);
284 nlh
= __mnlg_msg_prepare(nlg
, CTRL_CMD_GETFAMILY
,
285 NLM_F_REQUEST
| NLM_F_ACK
, GENL_ID_CTRL
, 1);
286 mnl_attr_put_strz(nlh
, CTRL_ATTR_FAMILY_NAME
, family_name
);
288 err
= mnlg_socket_send(nlg
, nlh
);
290 goto err_mnlg_socket_send
;
292 err
= mnlg_socket_recv_run(nlg
, get_family_id_cb
, &nlg
->id
);
294 goto err_mnlg_socket_recv_run
;
296 nlg
->version
= version
;
299 err_mnlg_socket_recv_run
:
300 err_mnlg_socket_send
:
302 mnl_socket_close(nlg
->nl
);
310 void mnlg_socket_close(struct mnlg_socket
*nlg
)
312 mnl_socket_close(nlg
->nl
);