1 /*
2 * msgbuff.c - netlink message buffer
3 *
4 * Data structures and code for flexible message buffer abstraction.
5 */
6
7 #include <string.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11
12 #include "../internal.h"
13 #include "netlink.h"
14 #include "msgbuff.h"
15
16 #define MAX_MSG_SIZE (4 << 20) /* 4 MB */
17
18 /**
19 * msgbuff_realloc() - reallocate buffer if needed
20 * @msgbuff: message buffer
21 * @new_size: requested minimum size (add MNL_SOCKET_BUFFER_SIZE if zero)
22 *
23 * Make sure allocated buffer has size at least @new_size. If @new_size is
24 * shorter than current size, do nothing. If @new_size is 0, grow buffer by
25 * MNL_SOCKET_BUFFER_SIZE. Fail if new size would exceed MAX_MSG_SIZE.
26 *
27 * Return: 0 on success or negative error code
28 */
msgbuff_realloc(struct nl_msg_buff * msgbuff,unsigned int new_size)29 int msgbuff_realloc(struct nl_msg_buff *msgbuff, unsigned int new_size)
30 {
31 unsigned int nlhdr_off, genlhdr_off, payload_off;
32 unsigned int old_size = msgbuff->size;
33 char *nbuff;
34
35 nlhdr_off = (char *)msgbuff->nlhdr - msgbuff->buff;
36 genlhdr_off = (char *)msgbuff->genlhdr - msgbuff->buff;
37 payload_off = (char *)msgbuff->payload - msgbuff->buff;
38
39 if (!new_size)
40 new_size = old_size + MNL_SOCKET_BUFFER_SIZE;
41 if (new_size <= old_size)
42 return 0;
43 if (new_size > MAX_MSG_SIZE)
44 return -EMSGSIZE;
45 nbuff = realloc(msgbuff->buff, new_size);
46 if (!nbuff) {
47 msgbuff->buff = NULL;
48 msgbuff->size = 0;
49 msgbuff->left = 0;
50 return -ENOMEM;
51 }
52 if (nbuff != msgbuff->buff) {
53 if (new_size > old_size)
54 memset(nbuff + old_size, '\0', new_size - old_size);
55 msgbuff->nlhdr = (struct nlmsghdr *)(nbuff + nlhdr_off);
56 msgbuff->genlhdr = (struct genlmsghdr *)(nbuff + genlhdr_off);
57 msgbuff->payload = nbuff + payload_off;
58 msgbuff->buff = nbuff;
59 }
60 msgbuff->size = new_size;
61 msgbuff->left += (new_size - old_size);
62
63 return 0;
64 }
65
66 /**
67 * msgbuff_append() - add contents of another message buffer
68 * @dest: target message buffer
69 * @src: source message buffer
70 *
71 * Append contents of @src at the end of @dest. Fail if target buffer cannot
72 * be reallocated to sufficient size.
73 *
74 * Return: 0 on success or negative error code.
75 */
msgbuff_append(struct nl_msg_buff * dest,struct nl_msg_buff * src)76 int msgbuff_append(struct nl_msg_buff *dest, struct nl_msg_buff *src)
77 {
78 unsigned int src_len = mnl_nlmsg_get_payload_len(src->nlhdr);
79 unsigned int dest_len = MNL_ALIGN(msgbuff_len(dest));
80 int ret;
81
82 src_len -= GENL_HDRLEN;
83 ret = msgbuff_realloc(dest, dest_len + src_len);
84 if (ret < 0)
85 return ret;
86 memcpy(mnl_nlmsg_get_payload_tail(dest->nlhdr), src->payload, src_len);
87 msgbuff_reset(dest, dest_len + src_len);
88
89 return 0;
90 }
91
92 /**
93 * ethnla_put - write a netlink attribute to message buffer
94 * @msgbuff: message buffer
95 * @type: attribute type
96 * @len: attribute payload length
97 * @data: attribute payload
98 *
99 * Appends a netlink attribute with header to message buffer, reallocates
100 * if needed. This is mostly used via specific ethnla_put_* wrappers for
101 * basic data types.
102 *
103 * Return: false on success, true on error (reallocation failed)
104 */
ethnla_put(struct nl_msg_buff * msgbuff,uint16_t type,size_t len,const void * data)105 bool ethnla_put(struct nl_msg_buff *msgbuff, uint16_t type, size_t len,
106 const void *data)
107 {
108 struct nlmsghdr *nlhdr = msgbuff->nlhdr;
109
110 while (!mnl_attr_put_check(nlhdr, msgbuff->left, type, len, data)) {
111 int ret = msgbuff_realloc(msgbuff, 0);
112
113 if (ret < 0)
114 return true;
115 }
116
117 return false;
118 }
119
120 /**
121 * ethnla_nest_start - start a nested attribute
122 * @msgbuff: message buffer
123 * @type: nested attribute type (NLA_F_NESTED is added automatically)
124 *
125 * Return: pointer to the nest attribute or null of error
126 */
ethnla_nest_start(struct nl_msg_buff * msgbuff,uint16_t type)127 struct nlattr *ethnla_nest_start(struct nl_msg_buff *msgbuff, uint16_t type)
128 {
129 struct nlmsghdr *nlhdr = msgbuff->nlhdr;
130 struct nlattr *attr;
131
132 do {
133 attr = mnl_attr_nest_start_check(nlhdr, msgbuff->left, type);
134 if (attr)
135 return attr;
136 } while (msgbuff_realloc(msgbuff, 0) == 0);
137
138 return NULL;
139 }
140
__ethnla_fill_header_phy(struct nl_msg_buff * msgbuff,uint16_t type,const char * devname,uint32_t phy_index,uint32_t flags)141 static bool __ethnla_fill_header_phy(struct nl_msg_buff *msgbuff, uint16_t type,
142 const char *devname, uint32_t phy_index,
143 uint32_t flags)
144 {
145 struct nlattr *nest;
146
147 nest = ethnla_nest_start(msgbuff, type);
148 if (!nest)
149 return true;
150
151 if ((devname &&
152 ethnla_put_strz(msgbuff, ETHTOOL_A_HEADER_DEV_NAME, devname)) ||
153 (flags &&
154 ethnla_put_u32(msgbuff, ETHTOOL_A_HEADER_FLAGS, flags)) ||
155 (phy_index &&
156 ethnla_put_u32(msgbuff, ETHTOOL_A_HEADER_PHY_INDEX, phy_index)))
157 goto err;
158
159 ethnla_nest_end(msgbuff, nest);
160 return false;
161
162 err:
163 ethnla_nest_cancel(msgbuff, nest);
164 return true;
165 }
166
167 /**
168 * ethnla_fill_header() - write standard ethtool request header to message
169 * @msgbuff: message buffer
170 * @type: attribute type for header nest
171 * @devname: device name (NULL to omit)
172 * @flags: request flags (omitted if 0)
173 *
174 * Return: pointer to the nest attribute or null of error
175 */
ethnla_fill_header(struct nl_msg_buff * msgbuff,uint16_t type,const char * devname,uint32_t flags)176 bool ethnla_fill_header(struct nl_msg_buff *msgbuff, uint16_t type,
177 const char *devname, uint32_t flags)
178 {
179 return __ethnla_fill_header_phy(msgbuff, type, devname, 0, flags);
180 }
181
182 /**
183 * ethnla_fill_header_phy() - write standard ethtool request header to message,
184 * targetting a device's phy
185 * @msgbuff: message buffer
186 * @type: attribute type for header nest
187 * @devname: device name (NULL to omit)
188 * @phy_index: phy index to target (0 to omit)
189 * @flags: request flags (omitted if 0)
190 *
191 * Return: pointer to the nest attribute or null of error
192 */
ethnla_fill_header_phy(struct nl_msg_buff * msgbuff,uint16_t type,const char * devname,uint32_t phy_index,uint32_t flags)193 bool ethnla_fill_header_phy(struct nl_msg_buff *msgbuff, uint16_t type,
194 const char *devname, uint32_t phy_index,
195 uint32_t flags)
196 {
197 return __ethnla_fill_header_phy(msgbuff, type, devname, phy_index,
198 flags);
199 }
200
201 /**
202 * __msg_init() - init a genetlink message, fill netlink and genetlink header
203 * @msgbuff: message buffer
204 * @family: genetlink family
205 * @cmd: genetlink command (genlmsghdr::cmd)
206 * @flags: netlink flags (nlmsghdr::nlmsg_flags)
207 * @version: genetlink family version (genlmsghdr::version)
208 *
209 * Initialize a new genetlink message, fill netlink and genetlink header and
210 * set pointers in struct nl_msg_buff.
211 *
212 * Return: 0 on success or negative error code.
213 */
__msg_init(struct nl_msg_buff * msgbuff,int family,int cmd,unsigned int flags,int version)214 int __msg_init(struct nl_msg_buff *msgbuff, int family, int cmd,
215 unsigned int flags, int version)
216 {
217 struct nlmsghdr *nlhdr;
218 struct genlmsghdr *genlhdr;
219 int ret;
220
221 ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
222 if (ret < 0)
223 return ret;
224 memset(msgbuff->buff, '\0', NLMSG_HDRLEN + GENL_HDRLEN);
225
226 nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
227 nlhdr->nlmsg_type = family;
228 nlhdr->nlmsg_flags = flags;
229 msgbuff->nlhdr = nlhdr;
230
231 genlhdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*genlhdr));
232 genlhdr->cmd = cmd;
233 genlhdr->version = version;
234 msgbuff->genlhdr = genlhdr;
235
236 msgbuff->payload = mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
237
238 return 0;
239 }
240
241 /**
242 * msg_init() - init an ethtool netlink message
243 * @msgbuff: message buffer
244 * @cmd: genetlink command (genlmsghdr::cmd)
245 * @flags: netlink flags (nlmsghdr::nlmsg_flags)
246 *
247 * Initialize a new ethtool netlink message, fill netlink and genetlink header
248 * and set pointers in struct nl_msg_buff.
249 *
250 * Return: 0 on success or negative error code.
251 */
msg_init(struct nl_context * nlctx,struct nl_msg_buff * msgbuff,int cmd,unsigned int flags)252 int msg_init(struct nl_context *nlctx, struct nl_msg_buff *msgbuff, int cmd,
253 unsigned int flags)
254 {
255 return __msg_init(msgbuff, nlctx->ethnl_fam, cmd, flags,
256 ETHTOOL_GENL_VERSION);
257 }
258
259 /**
260 * msgbuff_init() - initialize a message buffer
261 * @msgbuff: message buffer
262 *
263 * Initialize a message buffer structure before first use. Buffer length is
264 * set to zero and the buffer is not allocated until the first call to
265 * msgbuff_reallocate().
266 */
msgbuff_init(struct nl_msg_buff * msgbuff)267 void msgbuff_init(struct nl_msg_buff *msgbuff)
268 {
269 memset(msgbuff, '\0', sizeof(*msgbuff));
270 }
271
272 /**
273 * msg_done() - destroy a message buffer
274 * @msgbuff: message buffer
275 *
276 * Free the buffer and reset size and remaining size.
277 */
msgbuff_done(struct nl_msg_buff * msgbuff)278 void msgbuff_done(struct nl_msg_buff *msgbuff)
279 {
280 free(msgbuff->buff);
281 msgbuff->buff = NULL;
282 msgbuff->size = 0;
283 msgbuff->left = 0;
284 }
285