1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
4 */
5
6 #ifndef NETLINK_UTILS_PRIV_H_
7 #define NETLINK_UTILS_PRIV_H_
8
9 #include <byteswap.h>
10 #include <assert.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <stdbool.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17
18 #if __BYTE_ORDER == __BIG_ENDIAN
19 #define ntohll(x) (x)
20 #elif __BYTE_ORDER == __LITTLE_ENDIAN
21 #define ntohll(x) bswap_64((x))
22 #endif
23 #define htonll(x) ntohll(x)
24
25 /*****************************************************************************/
26
27 #define _NL_STRINGIFY_ARG(contents) #contents
28 #define _NL_STRINGIFY(macro_or_string) _NL_STRINGIFY_ARG (macro_or_string)
29
30 /*****************************************************************************/
31
32 #if defined (__GNUC__)
33 #define _NL_PRAGMA_WARNING_DO(warning) _NL_STRINGIFY(GCC diagnostic ignored warning)
34 #elif defined (__clang__)
35 #define _NL_PRAGMA_WARNING_DO(warning) _NL_STRINGIFY(clang diagnostic ignored warning)
36 #endif
37
38 /* you can only suppress a specific warning that the compiler
39 * understands. Otherwise you will get another compiler warning
40 * about invalid pragma option.
41 * It's not that bad however, because gcc and clang often have the
42 * same name for the same warning. */
43
44 #if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
45 #define _NL_PRAGMA_WARNING_DISABLE(warning) \
46 _Pragma("GCC diagnostic push") \
47 _Pragma(_NL_PRAGMA_WARNING_DO("-Wpragmas")) \
48 _Pragma(_NL_PRAGMA_WARNING_DO(warning))
49 #elif defined (__clang__)
50 #define _NL_PRAGMA_WARNING_DISABLE(warning) \
51 _Pragma("clang diagnostic push") \
52 _Pragma(_NL_PRAGMA_WARNING_DO("-Wunknown-warning-option")) \
53 _Pragma(_NL_PRAGMA_WARNING_DO(warning))
54 #else
55 #define _NL_PRAGMA_WARNING_DISABLE(warning)
56 #endif
57
58 #if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
59 #define _NL_PRAGMA_WARNING_REENABLE \
60 _Pragma("GCC diagnostic pop")
61 #elif defined (__clang__)
62 #define _NL_PRAGMA_WARNING_REENABLE \
63 _Pragma("clang diagnostic pop")
64 #else
65 #define _NL_PRAGMA_WARNING_REENABLE
66 #endif
67
68 /*****************************************************************************/
69
70 #define _nl_unused __attribute__ ((__unused__))
71 #define _nl_auto(fcn) __attribute__ ((__cleanup__(fcn)))
72
73 /*****************************************************************************/
74
75 #ifdef thread_local
76 #define _nl_thread_local thread_local
77 /*
78 * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
79 * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
80 */
81 #elif __STDC_VERSION__ >= 201112L && \
82 !(defined(__STDC_NO_THREADS__) || \
83 (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && \
84 __GLIBC_MINOR__ < 16))
85 #define _nl_thread_local _Thread_local
86 #else
87 #define _nl_thread_local __thread
88 #endif
89
90 /*****************************************************************************/
91
92 #define _NL_STATIC_ASSERT(cond) ((void) sizeof (char[(cond) ? 1 : -1]))
93
94 /*****************************************************************************/
95
96 #if defined(NL_MORE_ASSERTS) && NL_MORE_ASSERTS > 0
97 #define _nl_assert(cond) assert(cond)
98 #else
99 #define _nl_assert(cond) do { if (0) { assert(cond); } } while (0)
100 #endif
101
102 #define _nl_assert_not_reached() assert(0)
103
104 /*****************************************************************************/
105
106 #define _nl_assert_addr_family_or_unspec(addr_family) \
107 do { \
108 typeof(addr_family) _addr_family = (addr_family); \
109 \
110 _nl_assert(_addr_family == AF_UNSPEC || \
111 _addr_family == AF_INET || \
112 _addr_family == AF_INET6); \
113 } while (0)
114
115 #define _nl_assert_addr_family(addr_family) \
116 do { \
117 typeof(addr_family) _addr_family = (addr_family); \
118 \
119 _nl_assert(_addr_family == AF_INET || \
120 _addr_family == AF_INET6); \
121 } while (0)
122
123 /*****************************************************************************/
124
125 #define _NL_SWAP(pa, pb) \
126 do { \
127 typeof(*(pa)) *_pa = (pa); \
128 typeof(*(pb)) *_pb = (pb); \
129 typeof(*_pa) _tmp; \
130 \
131 _nl_assert(_pa); \
132 _nl_assert(_pb); \
133 _tmp = *_pa; \
134 *_pa = *_pb; \
135 *_pb = _tmp; \
136 } while (0)
137
138 /*****************************************************************************/
139
140 #define _NL_N_ELEMENTS(arr) (sizeof(arr) / sizeof((arr)[0]))
141
142 /*****************************************************************************/
143
144 extern const char *nl_strerror_l(int err);
145
146 /*****************************************************************************/
147
148 /* internal macro to calculate the size of a struct @type up to (and including) @field.
149 * this will be used for .minlen policy fields, so that we require only a field of up
150 * to the given size. */
151 #define _nl_offsetofend(type, field) (offsetof (type, field) + sizeof (((type *) NULL)->field))
152
153 /*****************************************************************************/
154
155 #define _nl_clear_pointer(pp, destroy) \
156 ({ \
157 __typeof__ (*(pp)) *_pp = (pp); \
158 __typeof__ (*_pp) _p; \
159 int _changed = 0; \
160 \
161 if ( _pp \
162 && (_p = *_pp)) { \
163 _nl_unused const void *const _p_check_is_pointer = _p; \
164 \
165 *_pp = NULL; \
166 \
167 (destroy) (_p); \
168 \
169 _changed = 1; \
170 } \
171 _changed; \
172 })
173
174 #define _nl_clear_free(pp) _nl_clear_pointer (pp, free)
175
176 #define _nl_steal_pointer(pp) \
177 ({ \
178 __typeof__ (*(pp)) *const _pp = (pp); \
179 __typeof__ (*_pp) _p = NULL; \
180 \
181 if ( _pp \
182 && (_p = *_pp)) { \
183 *_pp = NULL; \
184 } \
185 \
186 _p; \
187 })
188
189 /*****************************************************************************/
190
191 #define _nl_malloc_maybe_a(alloca_maxlen, bytes, to_free) \
192 ({ \
193 const size_t _bytes = (bytes); \
194 __typeof__ (to_free) _to_free = (to_free); \
195 __typeof__ (*_to_free) _ptr; \
196 \
197 _NL_STATIC_ASSERT ((alloca_maxlen) <= 500); \
198 _nl_assert (_to_free && !*_to_free); \
199 \
200 if (_bytes <= (alloca_maxlen)) { \
201 _ptr = alloca (_bytes); \
202 } else { \
203 _ptr = malloc (_bytes); \
204 *_to_free = _ptr; \
205 }; \
206 \
207 _ptr; \
208 })
209
210 /*****************************************************************************/
211
_nl_streq(const char * a,const char * b)212 static inline bool _nl_streq(const char *a, const char *b)
213 {
214 return !strcmp(a, b);
215 }
216
_nl_streq0(const char * a,const char * b)217 static inline bool _nl_streq0(const char *a, const char *b)
218 {
219 return a == b || (a && b && _nl_streq(a, b));
220 }
221
222 static inline char *
_nl_strncpy_trunc(char * dst,const char * src,size_t len)223 _nl_strncpy_trunc(char *dst, const char *src, size_t len)
224 {
225 /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL
226 * behavior of strncpy(). This is just strncpy() with gracefully handling truncation
227 * (and disabling the "-Wstringop-truncation" warning).
228 *
229 * Note that truncation is silently accepted.
230 */
231
232 _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
233 _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow");
234
235 if (len > 0) {
236 _nl_assert(dst);
237 _nl_assert(src);
238
239 strncpy(dst, src, len);
240
241 dst[len - 1] = '\0';
242 }
243
244 _NL_PRAGMA_WARNING_REENABLE;
245 _NL_PRAGMA_WARNING_REENABLE;
246
247 return dst;
248 }
249
250 static inline char *
_nl_strncpy_assert(char * dst,const char * src,size_t len)251 _nl_strncpy_assert(char *dst, const char *src, size_t len)
252 {
253 /* we don't use/reimplement strlcpy(), because we want the fill-all-with-NUL
254 * behavior of strncpy(). This is just strncpy() with assertion against truncation
255 * (and disabling the "-Wstringop-truncation" warning).
256 *
257 * Note that truncation is still a bug and there is an _nl_assert()
258 * against that.
259 */
260
261 _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
262 _NL_PRAGMA_WARNING_DISABLE ("-Wstringop-overflow");
263
264 if (len > 0) {
265 _nl_assert(dst);
266 _nl_assert(src);
267
268 strncpy(dst, src, len);
269
270 _nl_assert (dst[len - 1] == '\0');
271
272 dst[len - 1] = '\0';
273 }
274
275 _NL_PRAGMA_WARNING_REENABLE;
276 _NL_PRAGMA_WARNING_REENABLE;
277
278 return dst;
279 }
280
281 #include "nl-auto.h"
282
283 #define _NL_RETURN_ON_ERR(cmd) \
284 do { \
285 int _err; \
286 \
287 _err = (cmd); \
288 if (_err < 0) \
289 return _err; \
290 } while (0)
291
292 #define _NL_RETURN_E_ON_ERR(e, cmd) \
293 do { \
294 int _err; \
295 \
296 _err = (cmd); \
297 if (_err < 0) { \
298 _NL_STATIC_ASSERT((e) > 0); \
299 return -(e); \
300 } \
301 } while (0)
302
303 /* _NL_RETURN_ON_PUT_ERR() shall only be used with a put command (nla_put or nlmsg_append).
304 * These commands can either fail with a regular error code (which gets propagated)
305 * or with -NLE_NOMEM. However, they don't really try to allocate memory, so we don't
306 * want to propagate -NLE_NOMEM. Instead, we coerce such failure to -NLE_MSGSIZE. */
307 #define _NL_RETURN_ON_PUT_ERR(put_cmd) \
308 do { \
309 int _err; \
310 \
311 _err = (put_cmd); \
312 if (_err < 0) { \
313 if (_err == -NLE_NOMEM) { \
314 /* nla_put() returns -NLE_NOMEM in case of out of buffer size. We don't
315 * want to propagate that error and map it to -NLE_MSGSIZE. */ \
316 return -NLE_MSGSIZE; \
317 } \
318 /* any other error can only be due to invalid parameters. Propagate the
319 * error, however also assert that it cannot be reached. */ \
320 _nl_assert_not_reached (); \
321 return _err; \
322 } else \
323 _nl_assert (_err == 0); \
324 } while (0)
325
326 static inline int
_nl_close(int fd)327 _nl_close(int fd)
328 {
329 int r;
330
331 r = close(fd);
332 _nl_assert(r == 0 || fd < 0 || errno != EBADF);
333 return r;
334 }
335
336 static inline void *
_nl_memdup(const void * ptr,size_t len)337 _nl_memdup(const void *ptr, size_t len)
338 {
339 void *p;
340
341 if (len == 0) {
342 /* malloc() leaves it implementation defined whether to return NULL.
343 * Callers rely on returning NULL if len is zero. */
344 return NULL;
345 }
346
347 p = malloc(len);
348 if (!p)
349 return NULL;
350 memcpy(p, ptr, len);
351 return p;
352 }
353
354 #define _nl_memdup_ptr(ptr) ((__typeof__(ptr)) _nl_memdup((ptr), sizeof(*(ptr))))
355
356 /*****************************************************************************/
357
358 typedef union {
359 in_addr_t addr4;
360 struct in_addr a4;
361 struct in6_addr a6;
362 } _NLIPAddr;
363
_nl_inet_ntop(int addr_family,const void * addr,char buf[static INET_ADDRSTRLEN])364 static inline char *_nl_inet_ntop(int addr_family, const void *addr,
365 char buf[static INET_ADDRSTRLEN])
366 {
367 char *r;
368
369 _nl_assert_addr_family(addr_family);
370 _nl_assert(addr);
371
372 /* inet_ntop() is documented to fail, but if we pass a known address family
373 * and a suitably large buffer, it cannot. Assert for that. */
374
375 r = (char *)inet_ntop(addr_family, addr, buf,
376 (addr_family == AF_INET) ? INET_ADDRSTRLEN :
377 INET6_ADDRSTRLEN);
378 _nl_assert(r == buf);
379 _nl_assert(strlen(r) < ((addr_family == AF_INET) ? INET_ADDRSTRLEN :
380 INET6_ADDRSTRLEN));
381
382 return r;
383 }
384
_nl_inet_ntop_dup(int addr_family,const void * addr)385 static inline char *_nl_inet_ntop_dup(int addr_family, const void *addr)
386 {
387 return (char *)_nl_inet_ntop(addr_family, addr,
388 malloc((addr_family == AF_INET) ?
389 INET_ADDRSTRLEN :
390 INET6_ADDRSTRLEN));
391 }
392
393 #endif
394