xref: /freebsd/sys/netlink/netlink_message_writer.h (revision 9088779e3c8b810afb701adb80be154a7b2e0523)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 Ng Peng Nam Sean
5  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #ifndef _NETLINK_NETLINK_MESSAGE_WRITER_H_
30 #define _NETLINK_NETLINK_MESSAGE_WRITER_H_
31 
32 #ifdef _KERNEL
33 
34 #include <netinet/in.h>
35 
36 /*
37  * It is not meant to be included directly
38  */
39 
40 struct mbuf;
41 struct nl_writer;
42 typedef bool nl_writer_cb(struct nl_writer *nw, void *buf, int buflen, int cnt);
43 
44 struct nl_writer {
45 	int			alloc_len;	/* allocated buffer length */
46 	int			offset;		/* offset from the start of the buffer */
47 	struct nlmsghdr		*hdr;		/* Pointer to the currently-filled msg */
48 	char			*data;		/* pointer to the contiguous storage */
49 	void			*_storage;	/* Underlying storage pointer */
50 	nl_writer_cb		*cb;		/* Callback to flush data */
51 	union {
52 		void		*arg_ptr;	/* Callback argument as pointer */
53 		uint64_t	arg_uint;	/* Callback argument as int */
54 	};
55 	int			num_messages;	/* Number of messages in the buffer */
56 	int			malloc_flag;	/* M_WAITOK or M_NOWAIT */
57 	uint8_t			writer_type;	/* NS_WRITER_TYPE_* */
58 	uint8_t			writer_target;	/* NS_WRITER_TARGET_*  */
59 	bool			ignore_limit;	/* If true, ignores RCVBUF limit */
60 	bool			enomem;		/* True if ENOMEM occured */
61 	bool			suppress_ack;	/* If true, don't send NLMSG_ERR */
62 };
63 #define	NS_WRITER_TARGET_SOCKET	0
64 #define	NS_WRITER_TARGET_GROUP	1
65 #define	NS_WRITER_TARGET_CHAIN	2
66 
67 #define	NS_WRITER_TYPE_MBUF	0
68 #define NS_WRITER_TYPE_BUF	1
69 #define NS_WRITER_TYPE_LBUF	2
70 #define NS_WRITER_TYPE_MBUFC	3
71 #define NS_WRITER_TYPE_STUB	4
72 
73 
74 #define	NLMSG_SMALL	128
75 #define	NLMSG_LARGE	2048
76 
77 /* Message and attribute writing */
78 
79 struct nlpcb;
80 
81 #if defined(NETLINK) || defined(NETLINK_MODULE)
82 /* Provide optimized calls to the functions inside the same linking unit */
83 
84 bool _nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp);
85 bool _nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id);
86 bool _nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm);
87 bool _nlmsg_flush(struct nl_writer *nw);
88 void _nlmsg_ignore_limit(struct nl_writer *nw);
89 
90 bool _nlmsg_refill_buffer(struct nl_writer *nw, int required_size);
91 bool _nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
92     uint16_t flags, uint32_t len);
93 bool _nlmsg_end(struct nl_writer *nw);
94 void _nlmsg_abort(struct nl_writer *nw);
95 
96 bool _nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr);
97 
98 
99 static inline bool
100 nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp)
101 {
102 	return (_nlmsg_get_unicast_writer(nw, expected_size, nlp));
103 }
104 
105 static inline bool
106 nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id)
107 {
108 	return (_nlmsg_get_group_writer(nw, expected_size, proto, group_id));
109 }
110 
111 static inline bool
112 nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm)
113 {
114 	return (_nlmsg_get_chain_writer(nw, expected_size, pm));
115 }
116 
117 static inline bool
118 nlmsg_flush(struct nl_writer *nw)
119 {
120 	return (_nlmsg_flush(nw));
121 }
122 
123 static inline void
124 nlmsg_ignore_limit(struct nl_writer *nw)
125 {
126 	_nlmsg_ignore_limit(nw);
127 }
128 
129 static inline bool
130 nlmsg_refill_buffer(struct nl_writer *nw, int required_size)
131 {
132 	return (_nlmsg_refill_buffer(nw, required_size));
133 }
134 
135 static inline bool
136 nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
137     uint16_t flags, uint32_t len)
138 {
139 	return (_nlmsg_add(nw, portid, seq, type, flags, len));
140 }
141 
142 static inline bool
143 nlmsg_end(struct nl_writer *nw)
144 {
145 	return (_nlmsg_end(nw));
146 }
147 
148 static inline void
149 nlmsg_abort(struct nl_writer *nw)
150 {
151 	return (_nlmsg_abort(nw));
152 }
153 
154 static inline bool
155 nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr)
156 {
157 	return (_nlmsg_end_dump(nw, error, hdr));
158 }
159 
160 #else
161 /* Provide access to the functions via netlink_glue.c */
162 
163 bool nlmsg_get_unicast_writer(struct nl_writer *nw, int expected_size, struct nlpcb *nlp);
164 bool nlmsg_get_group_writer(struct nl_writer *nw, int expected_size, int proto, int group_id);
165 bool nlmsg_get_chain_writer(struct nl_writer *nw, int expected_size, struct mbuf **pm);
166 bool nlmsg_flush(struct nl_writer *nw);
167 void nlmsg_ignore_limit(struct nl_writer *nw);
168 
169 bool nlmsg_refill_buffer(struct nl_writer *nw, int required_size);
170 bool nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
171     uint16_t flags, uint32_t len);
172 bool nlmsg_end(struct nl_writer *nw);
173 void nlmsg_abort(struct nl_writer *nw);
174 
175 bool nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr);
176 
177 #endif /* defined(NETLINK) || defined(NETLINK_MODULE) */
178 
179 static inline bool
180 nlmsg_reply(struct nl_writer *nw, const struct nlmsghdr *hdr, int payload_len)
181 {
182 	return (nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, hdr->nlmsg_type,
183 	    hdr->nlmsg_flags, payload_len));
184 }
185 
186 #define nlmsg_data(_hdr)        ((void *)((_hdr) + 1))
187 
188 /*
189  * KPI similar to mtodo():
190  * current (uncompleted) header is guaranteed to be contiguous,
191  *  but can be reallocated, thus pointers may need to be readjusted.
192  */
193 static inline int
194 nlattr_save_offset(const struct nl_writer *nw)
195 {
196         return (nw->offset - ((char *)nw->hdr - nw->data));
197 }
198 
199 static inline void *
200 _nlattr_restore_offset(const struct nl_writer *nw, int off)
201 {
202 	return ((void *)((char *)nw->hdr + off));
203 }
204 #define	nlattr_restore_offset(_ns, _off, _t)	((_t *)_nlattr_restore_offset(_ns, _off))
205 
206 static inline void
207 nlattr_set_len(const struct nl_writer *nw, int off)
208 {
209 	struct nlattr *nla = nlattr_restore_offset(nw, off, struct nlattr);
210 	nla->nla_len = nlattr_save_offset(nw) - off;
211 }
212 
213 static inline void *
214 nlmsg_reserve_data_raw(struct nl_writer *nw, size_t sz)
215 {
216 	sz = NETLINK_ALIGN(sz);
217 
218         if (__predict_false(nw->offset + sz > nw->alloc_len)) {
219 		if (!nlmsg_refill_buffer(nw, sz))
220 			return (NULL);
221         }
222 
223         void *data_ptr = &nw->data[nw->offset];
224         nw->offset += sz;
225 
226         return (data_ptr);
227 }
228 #define nlmsg_reserve_object(_ns, _t)	((_t *)nlmsg_reserve_data_raw(_ns, sizeof(_t)))
229 #define nlmsg_reserve_data(_ns, _sz, _t)	((_t *)nlmsg_reserve_data_raw(_ns, _sz))
230 
231 static inline int
232 nlattr_add_nested(struct nl_writer *nw, uint16_t nla_type)
233 {
234 	int off = nlattr_save_offset(nw);
235 	struct nlattr *nla = nlmsg_reserve_data(nw, sizeof(struct nlattr), struct nlattr);
236 	if (__predict_false(nla == NULL))
237 		return (0);
238 	nla->nla_type = nla_type;
239 	return (off);
240 }
241 
242 static inline void *
243 _nlmsg_reserve_attr(struct nl_writer *nw, uint16_t nla_type, uint16_t sz)
244 {
245 	sz += sizeof(struct nlattr);
246 
247 	struct nlattr *nla = nlmsg_reserve_data(nw, sz, struct nlattr);
248 	if (__predict_false(nla == NULL))
249 		return (NULL);
250 	nla->nla_type = nla_type;
251 	nla->nla_len = sz;
252 
253 	return ((void *)(nla + 1));
254 }
255 #define	nlmsg_reserve_attr(_ns, _at, _t)	((_t *)_nlmsg_reserve_attr(_ns, _at, NLA_ALIGN(sizeof(_t))))
256 
257 static inline bool
258 nlattr_add(struct nl_writer *nw, int attr_type, int attr_len, const void *data)
259 {
260 	int required_len = NLA_ALIGN(attr_len + sizeof(struct nlattr));
261 
262         if (__predict_false(nw->offset + required_len > nw->alloc_len)) {
263 		if (!nlmsg_refill_buffer(nw, required_len))
264 			return (false);
265 	}
266 
267         struct nlattr *nla = (struct nlattr *)(&nw->data[nw->offset]);
268 
269         nla->nla_len = attr_len + sizeof(struct nlattr);
270         nla->nla_type = attr_type;
271         if (attr_len > 0) {
272 		if ((attr_len % 4) != 0) {
273 			/* clear padding bytes */
274 			bzero((char *)nla + required_len - 4, 4);
275 		}
276                 memcpy((nla + 1), data, attr_len);
277 	}
278         nw->offset += required_len;
279         return (true);
280 }
281 
282 static inline bool
283 nlattr_add_raw(struct nl_writer *nw, const struct nlattr *nla_src)
284 {
285 	int attr_len = nla_src->nla_len - sizeof(struct nlattr);
286 
287 	MPASS(attr_len >= 0);
288 
289 	return (nlattr_add(nw, nla_src->nla_type, attr_len, (const void *)(nla_src + 1)));
290 }
291 
292 static inline bool
293 nlattr_add_u8(struct nl_writer *nw, int attrtype, uint8_t value)
294 {
295 	return (nlattr_add(nw, attrtype, sizeof(uint8_t), &value));
296 }
297 
298 static inline bool
299 nlattr_add_u16(struct nl_writer *nw, int attrtype, uint16_t value)
300 {
301 	return (nlattr_add(nw, attrtype, sizeof(uint16_t), &value));
302 }
303 
304 static inline bool
305 nlattr_add_u32(struct nl_writer *nw, int attrtype, uint32_t value)
306 {
307 	return (nlattr_add(nw, attrtype, sizeof(uint32_t), &value));
308 }
309 
310 static inline bool
311 nlattr_add_u64(struct nl_writer *nw, int attrtype, uint64_t value)
312 {
313 	return (nlattr_add(nw, attrtype, sizeof(uint64_t), &value));
314 }
315 
316 static inline bool
317 nlattr_add_s8(struct nl_writer *nw, int attrtype, int8_t value)
318 {
319 	return (nlattr_add(nw, attrtype, sizeof(int8_t), &value));
320 }
321 
322 static inline bool
323 nlattr_add_s16(struct nl_writer *nw, int attrtype, int16_t value)
324 {
325 	return (nlattr_add(nw, attrtype, sizeof(int16_t), &value));
326 }
327 
328 static inline bool
329 nlattr_add_s32(struct nl_writer *nw, int attrtype, int32_t value)
330 {
331 	return (nlattr_add(nw, attrtype, sizeof(int32_t), &value));
332 }
333 
334 static inline bool
335 nlattr_add_s64(struct nl_writer *nw, int attrtype, int64_t value)
336 {
337 	return (nlattr_add(nw, attrtype, sizeof(int64_t), &value));
338 }
339 
340 static inline bool
341 nlattr_add_flag(struct nl_writer *nw, int attrtype)
342 {
343 	return (nlattr_add(nw, attrtype, 0, NULL));
344 }
345 
346 static inline bool
347 nlattr_add_string(struct nl_writer *nw, int attrtype, const char *str)
348 {
349 	return (nlattr_add(nw, attrtype, strlen(str) + 1, str));
350 }
351 
352 static inline bool
353 nlattr_add_in_addr(struct nl_writer *nw, int attrtype, const struct in_addr *in)
354 {
355 	return (nlattr_add(nw, attrtype, sizeof(*in), in));
356 }
357 
358 static inline bool
359 nlattr_add_in6_addr(struct nl_writer *nw, int attrtype, const struct in6_addr *in6)
360 {
361 	return (nlattr_add(nw, attrtype, sizeof(*in6), in6));
362 }
363 #endif
364 #endif
365